自定义可交互的转场动画

自定义转场动画主要有以下步骤
  • 自定义导航栏
  • 自定义跳转动画
  • 实现交互(UIPercentDrivenInteractiveTransition协议)
  1. 自定义导航栏:自定义导航栏需要遵守协议,该协议主要有如下两个方法

    // 该方法返回导航跳转时的动画,如果返回nil,则是系统默认的跳转动画,并且通过operation来判断当前执行push还是pop
    -(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
    animationControllerForOperation:(UINavigationControllerOperation)operation
    fromViewController:(UIViewController *)fromVC
    toViewController:(UIViewController *)toVC
    {
        return nil;
    }

    // 该方法返回自定义交互动画,只有实现该协议,才能在跳转时实现与用户交互
    -(id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
    interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
    {
        return nil;
    }
  2. 自定义跳转动画:自定义跳转动画需要遵守协议,该协议主要实现下面的方法

    // 该方法返回动画执行的时间,transitionContext由系统获取
    -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.3;
    }

    // 通过该协议,获取跳转的两个VC
    -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        [self animateTransition:transitionContext fromVC:fromVC toVC:toVC fromView:fromVC.view toView:toVC.view];
    }

    // 这是模仿系统导航跳转的例子
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    fromVC:(UIViewController *)fromVC
    toVC:(UIViewController *)toVC
    fromView:(UIView *)fromView toView:(UIView *)toView
    {
        [[transitionContext containerView] addSubview:toView];
        if (_isPush) {
            [[transitionContext containerView] bringSubviewToFront:toView];
            toView.transform = CGAffineTransformMakeTranslation(-toView.bounds.size.width, 0);
        } else {
            [[transitionContext containerView] bringSubviewToFront:fromView];
        }
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            if (_isPush) {
                  toView.transform = CGAffineTransformIdentity;
            } else {
                  fromView.transform = CGAffineTransformMakeTranslation(-fromView.bounds.size.width, 0);
            }
        } completion:^(BOOL finished) {
            transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }
  3. 实现交互:自定义一个继承自UIPercentDrivenInteractiveTransition的类,它遵守协议,我们可以告诉它当前转场动画进行的百分比,它再来更新动画的进度。转场动画进行的百分比可以通过手势来计算

    // 1.在view上加入pan手势
    UIPanGestureRecognizer *gesture = objc_getAssociatedObject(self.interactiveVC.view, (__bridge const void *)(kpanGestureKey));
    if (gesture) {
        [self.interactiveVC.view removeGestureRecognizer:gesture];
    }
    gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    [self.interactiveVC.view addGestureRecognizer:gesture];
    objc_setAssociatedObject(self.interactiveVC.view, (__bridge const void *)(kpanGestureKey), gesture,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    // 2.当pan手势移动,通过移动的距离来计算当前跳转动画需要完成的百分比,当手势结束时,判断动画完成的百分比
    // 如果>50%,finishInteractiveTransition完成跳转动画
    // 如果<50%,cancelInteractiveTransition取消动画,返回原样
    case UIGestureRecognizerStateChanged:
    CGFloat fraction = point.x / [[UIScreen mainScreen] bounds].size.width;
    fraction = fminf(fmaxf(fraction, 0.0), 1.0);
    self.shouldComplete = (fraction > 0.5);
    [self updateInteractiveTransition:fraction];
    break;
    case UIGestureRecognizerStateEnded:
    case UIGestureRecognizerStateCancelled:
    self.interactioning = NO;
    if (!self.shouldComplete || pan.state == UIGestureRecognizerStateCancelled) {
          [self cancelInteractiveTransition];
    }else {
          [self finishInteractiveTransition];
    }
    break;
  4. 最后附上Demo的地址:https://github.com/cdcyd/CommonControlsCollection