diff --git a/ZFDragableModalTransition/ZFModalTransitionAnimator.h b/ZFDragableModalTransition/ZFModalTransitionAnimator.h index f724e90..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; @@ -29,6 +36,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 28b85ae..247e220 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; @@ -30,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 @@ -133,25 +136,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) { @@ -192,23 +207,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]; + } } } @@ -235,6 +262,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,7 +272,13 @@ - (void)handlePan:(UIPanGestureRecognizer *)recognizer CGFloat animationRatio = 0; if (self.direction == ZFModalTransitonDirectionBottom) { - animationRatio = (location.y - self.panLocationStart) / (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)); } else if (self.direction == ZFModalTransitonDirectionRight) { @@ -254,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; } } @@ -381,50 +417,73 @@ - (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:UIViewAnimationOptionCurveEaseOut + animations:animations + completion:completion]; + } } - (void)cancelInteractiveTransition { id transitionContext = self.transitionContext; - [transitionContext 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 @@ -459,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) { @@ -528,7 +595,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]; @@ -540,13 +609,8 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event return; } - CGFloat topVerticalOffset = -self.scrollview.contentInset.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; }