From 68dce6a027604eb7c42b978aac8245f88c662bfc Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 18:59:46 -0500 Subject: [PATCH 1/7] Use .bounces. --- .../ZFModalTransitionAnimator.m | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index 28b85ae..f187c08 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -381,29 +381,40 @@ - (void)finishInteractiveTransition [toViewController beginAppearanceTransition:YES animated:YES]; } - [UIView animateWithDuration:[self transitionDuration:transitionContext] - delay:0 - usingSpringWithDamping:0.8 - initialSpringVelocity:0.1 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - CGFloat scaleBack = (1 / self.behindViewScale); - toViewController.view.layer.transform = CATransform3DScale(self.tempTransform, scaleBack, scaleBack, 1); - toViewController.view.alpha = 1.0f; - fromViewController.view.frame = endRect; - } completion:^(BOOL finished) { - if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) { - [toViewController endAppearanceTransition]; - } - [transitionContext completeTransition:YES]; - }]; + void (^animations)() = ^{ + CGFloat scaleBack = (1 / self.behindViewScale); + toViewController.view.layer.transform = CATransform3DScale(self.tempTransform, scaleBack, scaleBack, 1); + fromViewController.view.alpha = 1.0f; + fromViewController.view.frame = endRect; + }; + void (^completion)(BOOL) = ^(BOOL finished){ + if (toViewController.modalPresentationStyle == UIModalPresentationCustom) { + [fromViewController endAppearanceTransition]; + } + [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; + }; + + if(self.bounces){ + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0 + usingSpringWithDamping:0.8 + initialSpringVelocity:0.1 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } else { + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:animations + completion:completion]; + } } - (void)cancelInteractiveTransition { id transitionContext = self.transitionContext; - [transitionContext cancelInteractiveTransition]; - + UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; From 72cb6e5833124ec8b817bf97cb3cb277cac4ca04 Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 19:01:45 -0500 Subject: [PATCH 2/7] iOS 11, safeAreaInsets.top. --- ZFDragableModalTransition/ZFModalTransitionAnimator.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index f187c08..17abb4e 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -552,6 +552,11 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event } CGFloat topVerticalOffset = -self.scrollview.contentInset.top; + if ([self.scrollview respondsToSelector:@selector(safeAreaInsets)]) { + if (@available(iOS 11.0, *)) { + topVerticalOffset -= self.scrollview.safeAreaInsets.top; + } + } if ((fabs(velocity.x) < fabs(velocity.y)) && (nowPoint.y > prevPoint.y) && (self.scrollview.contentOffset.y <= topVerticalOffset)) { self.isFail = @NO; From 15204e5baae0a1e43db3997465a272be09930de6 Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 20:40:29 -0500 Subject: [PATCH 3/7] Avoid moving up when it's ZFModalTransitonDirectionBottom mode. --- ZFDragableModalTransition/ZFModalTransitionAnimator.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index 17abb4e..d890470 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -244,7 +244,10 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer CGFloat animationRatio = 0; if (self.direction == ZFModalTransitonDirectionBottom) { - animationRatio = (location.y - self.panLocationStart) / (CGRectGetHeight([self.modalController view].bounds)); + CGFloat moveYDrection = location.y - self.panLocationStart; + if (moveYDrection > 0) { + animationRatio = (moveYDrection) / (CGRectGetHeight([self.modalController view].bounds)); + } } else if (self.direction == ZFModalTransitonDirectionLeft) { animationRatio = (self.panLocationStart - location.x) / (CGRectGetWidth([self.modalController view].bounds)); } else if (self.direction == ZFModalTransitonDirectionRight) { From 74652b14e9aa8675bbe2fd02e4388204888e54c0 Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 22:00:05 -0500 Subject: [PATCH 4/7] Keep tracking dragging when moving up. --- .../ZFModalTransitionAnimator.m | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index d890470..65ed978 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -12,6 +12,7 @@ @interface ZFModalTransitionAnimator () @property (nonatomic, strong) ZFDetectScrollViewEndGestureRecognizer *gesture; @property (nonatomic, strong) id transitionContext; @property CGFloat panLocationStart; +@property CGFloat scrollViewOffsetStart; @property BOOL isDismiss; @property BOOL isInteractive; @property CATransform3D tempTransform; @@ -235,6 +236,7 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer self.isInteractive = YES; if (self.direction == ZFModalTransitonDirectionBottom) { self.panLocationStart = location.y; + self.scrollViewOffsetStart = self.gesture.scrollview.contentOffset.y; } else { self.panLocationStart = location.x; } @@ -244,9 +246,12 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer CGFloat animationRatio = 0; if (self.direction == ZFModalTransitonDirectionBottom) { - CGFloat moveYDrection = location.y - self.panLocationStart; - if (moveYDrection > 0) { - animationRatio = (moveYDrection) / (CGRectGetHeight([self.modalController view].bounds)); + CGFloat yOffset = location.y - self.panLocationStart; + BOOL isMovingDown = yOffset > 0; + if (isMovingDown) { + animationRatio = (yOffset) / (CGRectGetHeight([self.modalController view].bounds)); + } else { + self.gesture.scrollview.contentOffset = CGPointMake(self.gesture.scrollview.contentOffset.x, self.scrollViewOffsetStart - yOffset); } } else if (self.direction == ZFModalTransitonDirectionLeft) { animationRatio = (self.panLocationStart - location.x) / (CGRectGetWidth([self.modalController view].bounds)); @@ -542,7 +547,9 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event return; } - if (self.state == UIGestureRecognizerStateFailed) return; + if (self.state == UIGestureRecognizerStateFailed) { + return; + } CGPoint velocity = [self velocityInView:self.view]; CGPoint nowPoint = [touches.anyObject locationInView:self.view]; CGPoint prevPoint = [touches.anyObject previousLocationInView:self.view]; @@ -554,18 +561,8 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event return; } - CGFloat topVerticalOffset = -self.scrollview.contentInset.top; - if ([self.scrollview respondsToSelector:@selector(safeAreaInsets)]) { - if (@available(iOS 11.0, *)) { - topVerticalOffset -= self.scrollview.safeAreaInsets.top; - } - } - - if ((fabs(velocity.x) < fabs(velocity.y)) && (nowPoint.y > prevPoint.y) && (self.scrollview.contentOffset.y <= topVerticalOffset)) { + if ((fabs(velocity.x) < fabs(velocity.y)) && (nowPoint.y > prevPoint.y)) { self.isFail = @NO; - } else if (self.scrollview.contentOffset.y >= topVerticalOffset) { - self.state = UIGestureRecognizerStateFailed; - self.isFail = @YES; } else { self.isFail = @NO; } From e0b8ca1c9eed0b04f912a87c135d8d18d922d1af Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 22:06:58 -0500 Subject: [PATCH 5/7] Make sure .bounces is used everywhere. --- .../ZFModalTransitionAnimator.m | 146 +++++++++++------- 1 file changed, 91 insertions(+), 55 deletions(-) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index 65ed978..59f4d56 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -134,25 +134,37 @@ - (void)animateTransition:(id)transitionCo if (toViewController.modalPresentationStyle == UIModalPresentationCustom) { [fromViewController beginAppearanceTransition:NO animated:YES]; } - - [UIView animateWithDuration:[self transitionDuration:transitionContext] - delay:0 - usingSpringWithDamping:0.8 - initialSpringVelocity:0.1 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - fromViewController.view.transform = CGAffineTransformScale(fromViewController.view.transform, self.behindViewScale, self.behindViewScale); - fromViewController.view.alpha = self.behindViewAlpha; - - toViewController.view.frame = CGRectMake(0,0, - CGRectGetWidth(toViewController.view.frame), - CGRectGetHeight(toViewController.view.frame)); - } completion:^(BOOL finished) { - if (toViewController.modalPresentationStyle == UIModalPresentationCustom) { - [fromViewController endAppearanceTransition]; - } - [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; - }]; + + void (^animations)() = ^{ + fromViewController.view.transform = CGAffineTransformScale(fromViewController.view.transform, self.behindViewScale, self.behindViewScale); + fromViewController.view.alpha = self.behindViewAlpha; + + toViewController.view.frame = CGRectMake(0,0, + CGRectGetWidth(toViewController.view.frame), + CGRectGetHeight(toViewController.view.frame)); + }; + void (^completion)(BOOL) = ^(BOOL finished){ + if (toViewController.modalPresentationStyle == UIModalPresentationCustom) { + [fromViewController endAppearanceTransition]; + } + [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; + }; + + if(self.bounces){ + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0 + usingSpringWithDamping:0.8 + initialSpringVelocity:0.1 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } else { + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } } else { if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) { @@ -193,23 +205,35 @@ - (void)animateTransition:(id)transitionCo [toViewController beginAppearanceTransition:YES animated:YES]; } - [UIView animateWithDuration:[self transitionDuration:transitionContext] - delay:0 - usingSpringWithDamping:0.8 - initialSpringVelocity:0.1 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - CGFloat scaleBack = (1 / self.behindViewScale); - toViewController.view.layer.transform = CATransform3DScale(toViewController.view.layer.transform, scaleBack, scaleBack, 1); - toViewController.view.alpha = 1.0f; - fromViewController.view.frame = endRect; - } completion:^(BOOL finished) { - toViewController.view.layer.transform = CATransform3DIdentity; - if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) { - [toViewController endAppearanceTransition]; - } - [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; - }]; + void (^animations)() = ^{ + CGFloat scaleBack = (1 / self.behindViewScale); + toViewController.view.layer.transform = CATransform3DScale(toViewController.view.layer.transform, scaleBack, scaleBack, 1); + toViewController.view.alpha = 1.0f; + fromViewController.view.frame = endRect; + }; + void (^completion)(BOOL) = ^(BOOL finished){ + toViewController.view.layer.transform = CATransform3DIdentity; + if (fromViewController.modalPresentationStyle == UIModalPresentationCustom) { + [toViewController endAppearanceTransition]; + } + [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; + }; + + if(self.bounces){ + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0 + usingSpringWithDamping:0.8 + initialSpringVelocity:0.1 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } else { + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } } } @@ -413,7 +437,7 @@ - (void)finishInteractiveTransition } else { [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 - options:UIViewAnimationOptionCurveLinear + options:UIViewAnimationOptionCurveEaseOut animations:animations completion:completion]; } @@ -426,24 +450,36 @@ - (void)cancelInteractiveTransition UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; - [UIView animateWithDuration:0.4 - delay:0 - usingSpringWithDamping:0.8 - initialSpringVelocity:0.1 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - toViewController.view.layer.transform = self.tempTransform; - toViewController.view.alpha = self.behindViewAlpha; - - fromViewController.view.frame = CGRectMake(0,0, - CGRectGetWidth(fromViewController.view.frame), - CGRectGetHeight(fromViewController.view.frame)); - } completion:^(BOOL finished) { - [transitionContext completeTransition:NO]; - if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) { - [toViewController.view removeFromSuperview]; - } - }]; + void (^animations)() = ^{ + toViewController.view.layer.transform = self.tempTransform; + toViewController.view.alpha = self.behindViewAlpha; + + fromViewController.view.frame = CGRectMake(0,0, + CGRectGetWidth(fromViewController.view.frame), + CGRectGetHeight(fromViewController.view.frame)); + }; + void (^completion)(BOOL) = ^(BOOL finished){ + [transitionContext completeTransition:NO]; + if (fromViewController.modalPresentationStyle == UIModalPresentationFullScreen) { + [toViewController.view removeFromSuperview]; + } + }; + + if(self.bounces){ + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0 + usingSpringWithDamping:0.8 + initialSpringVelocity:0.1 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } else { + [UIView animateWithDuration:[self transitionDuration:transitionContext] + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } } #pragma mark - UIViewControllerTransitioningDelegate Methods From 110b1714409144abde7c752cb6a7c47f19beea83 Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Thu, 18 Jan 2018 22:16:18 -0500 Subject: [PATCH 6/7] Customizable dismiss velocity and distance. --- .../ZFModalTransitionAnimator.h | 2 ++ .../ZFModalTransitionAnimator.m | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.h b/ZFDragableModalTransition/ZFModalTransitionAnimator.h index f724e90..8962265 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.h +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.h @@ -29,6 +29,8 @@ typedef NS_ENUM(NSUInteger, ZFModalTransitonDirection) { @property CGFloat behindViewScale; @property CGFloat behindViewAlpha; @property CGFloat transitionDuration; +@property CGFloat dismissVelocity; +@property CGFloat dismissDistance; - (id)initWithModalViewController:(UIViewController *)modalViewController; - (void)setContentScrollView:(UIScrollView *)scrollView; diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index 59f4d56..7d60632 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -31,6 +31,8 @@ - (instancetype)initWithModalViewController:(UIViewController *)modalViewControl _behindViewScale = 0.9f; _behindViewAlpha = 1.0f; _transitionDuration = 0.8f; + _dismissVelocity = 100.0f; + _dismissDistance = 100.0f; [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -286,24 +288,26 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer [self updateInteractiveTransition:animationRatio]; } else if (recognizer.state == UIGestureRecognizerStateEnded) { - CGFloat velocityForSelectedDirection; - + CGFloat distanceOnSelectedDirection; if (self.direction == ZFModalTransitonDirectionBottom) { velocityForSelectedDirection = velocity.y; + distanceOnSelectedDirection = location.y - self.panLocationStart; } else { - velocityForSelectedDirection = velocity.x; + if (self.direction == ZFModalTransitonDirectionLeft) { + velocityForSelectedDirection = -velocity.x; + distanceOnSelectedDirection = self.panLocationStart - location.x; + } else if (self.direction == ZFModalTransitonDirectionRight) { + velocityForSelectedDirection = velocity.x; + distanceOnSelectedDirection = location.x - self.panLocationStart; + } } - if (velocityForSelectedDirection > 100 - && (self.direction == ZFModalTransitonDirectionRight - || self.direction == ZFModalTransitonDirectionBottom)) { - [self finishInteractiveTransition]; - } else if (velocityForSelectedDirection < -100 && self.direction == ZFModalTransitonDirectionLeft) { - [self finishInteractiveTransition]; - } else { - [self cancelInteractiveTransition]; - } + if (velocityForSelectedDirection > _dismissVelocity || distanceOnSelectedDirection > _dismissDistance) { + [self finishInteractiveTransition]; + } else { + [self cancelInteractiveTransition]; + } self.isInteractive = NO; } } From f51dde80fa2b7c79358c5186c7a3b998a8be9b51 Mon Sep 17 00:00:00 2001 From: CocoaBob Date: Sat, 20 Jan 2018 22:00:17 -0500 Subject: [PATCH 7/7] Added a delegate to decide if the pan gesture should begin. --- ZFDragableModalTransition/ZFModalTransitionAnimator.h | 7 +++++++ ZFDragableModalTransition/ZFModalTransitionAnimator.m | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.h b/ZFDragableModalTransition/ZFModalTransitionAnimator.h index 8962265..1a83f2e 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.h +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.h @@ -19,8 +19,15 @@ typedef NS_ENUM(NSUInteger, ZFModalTransitonDirection) { @property (nonatomic, weak) UIScrollView *scrollview; @end +@class ZFModalTransitionAnimator; + +@protocol ZFModalTransitionAnimatorDelegate +- (BOOL)modalTransitionAnimatorShouldBegin:(ZFModalTransitionAnimator *)animator; +@end + @interface ZFModalTransitionAnimator : UIPercentDrivenInteractiveTransition +@property (nonatomic, weak) id delegate; @property (nonatomic, assign, getter=isDragable) BOOL dragable; @property (nonatomic, readonly) ZFDetectScrollViewEndGestureRecognizer *gesture; @property (nonatomic, assign) UIGestureRecognizer *gestureRecognizerToFailPan; diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.m b/ZFDragableModalTransition/ZFModalTransitionAnimator.m index 7d60632..247e220 100644 --- a/ZFDragableModalTransition/ZFModalTransitionAnimator.m +++ b/ZFDragableModalTransition/ZFModalTransitionAnimator.m @@ -518,6 +518,14 @@ - (void)cancelInteractiveTransition #pragma mark - Gesture Delegate +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + if (self.delegate) { + return [self.delegate modalTransitionAnimatorShouldBegin:self]; + } + return YES; +} + - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if (self.direction == ZFModalTransitonDirectionBottom) {