iOS transition转场动画

  • Post author:
  • Post category:其他

转场动画

  1. 我们需要自定义一个遵循的协议的动画过渡管理对象,并实现两个必须实现的方法.
//返回动画事件
 - (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//所有的过渡动画事务都在这个方法里面完成
- (void)animateTransition:(id)transitionContext;
  1. 我们还需要自定义一个继承于UIPercentDrivenInteractiveTransition的手势过渡管理对象,我把它成为百分比手势过渡管理对象,因为动画的过程是通过百分比控制.

  2. 成为相应的代理,实现相应的代理方法,返回我们前两步自定义的对象就OK了.

转场动画实现的原理

  • 我们以present为例子,如果有其他的也可以用相同的方法实现.

  • 首先,通过viewControllerForKey去出转场前后的两个控制器:

UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  • snapshotViewAfterScreenUpdates可以对某个视图截图,我们采用对这个截图做动画代替直接对fromVC做动画,因为在手势过渡中直接使用fromVC动画会和手势有冲突, 如果不需要实现手势的话,就可以不使用截图视图了,大家可以自行尝试一下.
UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:NO];
tempView.frame = fromVC.view.frame;
  • 因为我们需要对截图做动画,那么fromVC.view就可以隐藏了.
    fromVC.view.hidden = YES;

  • 这里有个重要的概念containerView,如果要对视图做转场动画,视图就必须要加入containerView中才能进行,可以理解containerView管理着所有做转场动画的视图.

  • 将截图视图和toVC.view都加入到contianerView.


[containerView addSubview:tempView];

[containerView addSubview:toVC.view];

  • 设置toVC的frame,因为这里toVC present出来不是全屏,且初始的时候在底部,如果不设置frame的话默认就是整个屏幕咯,这里containerView的frame就是整个屏幕.

  • 现在,我们可以去设置转场动画啦

//通过viewControllerForKey取出转场前后的两个控制器
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        //snapshotViewAfterScreenUpdates可以对某个视图截图,我们采用对这个截图做动画代替直接对toVC做动画,因为在手势过渡中直接使用toVC动画会和手势有冲突, 如果不需要实现手势的话,就可以不是用截图视图了,大家可以自行尝试一下
        UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:NO];
        tempView.frame = fromVC.view.frame;
        //因为对截图做动画,vc1就可以隐藏了
        fromVC.view.hidden = YES;
        //这里有个重要的概念containerView,如果要对视图做转场动画,视图就必须要加入containerView中才能进行,可以理解containerView管理着所有做转场动画的视图
        UIView *containerView = [transitionContext containerView];
        //将截图视图和vc2的view都加入ContainerView中
        [containerView addSubview:tempView];
        [containerView addSubview:toVC.view];
        //设置vc2的frame,因为这里vc2present出来不是全屏,且初始的时候在底部,如果不设置frame的话默认就是整个屏幕咯,这里containerView的frame就是整个屏幕
        toVC.view.frame = CGRectMake(0, containerView.height, containerView.width, 400);
        //开始动画吧,使用产生弹簧效果的动画API
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.55 initialSpringVelocity:1.0 / 0.55 options:0 animations:^{
            //首先我们让vc2向上移动
            toVC.view.transform = CGAffineTransformMakeTranslation(0, -400);
            //然后让截图视图缩小一点即可
            tempView.transform = CGAffineTransformMakeScale(0.85, 0.85);
        } completion:^(BOOL finished) {
            //使用如下代码标记整个转场过程是否正常完成[transitionContext transitionWasCancelled]代表手势是否取消了,如果取消了就传NO表示转场失败,反之亦然,如果不用手势present的话直接传YES也是可以的,但是无论如何我们都必须标记转场的状态,系统才知道处理转场后的操作,否者认为你一直还在转场中,会出现无法交互的情况,切记!
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
            //转场失败后的处理
            if ([transitionContext transitionWasCancelled]) {
                //失败后,我们要把vc1显示出来
                fromVC.view.hidden = NO;
                //然后移除截图视图,因为下次触发present会重新截图
                [tempView removeFromSuperview];
            }
         }];
        }
  • 使用如下代码标记整个转场过程是否正常完成[transitionContext transitionWasCancelled]代表手势是否取消了,如果取消了就传NO表示转场失败,反之亦然,如果不用手势present的话直接传YES也是可以的,但是无论如何我们都必须标记转场的状态,系统才知道处理转场后的操作,否则认为你一直还在转场中,会出现无法交互的情况,切记!

[transitionContext completeTransition:![transitionContext transitionWasCancelled]];

        //转场失败后的处理

        if ([transitionContext transitionWasCancelled]) {

            //失败后,我们要把vc1显示出来

            fromVC.view.hidden = NO;

            //然后移除截图视图,因为下次触发present会重新截图

            [tempView removeFromSuperview];

        }

补充说明:
如果说,我们想使用push去用到自定义present转场动画类,我们可以在VC1->VC2的VC1中去实现UINavigationControllerDelegate协议中的一个方法navigationController: animationControllerForOperation: fromViewController: toViewController:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(nonnull UIViewController *)fromVC toViewController:(nonnull UIViewController *)toVC{
    if (operation == UINavigationControllerOperationPush){ // 就是在这里判断是哪种动画类型
        //这里的PresentTransition就是自定义present方法的动画,
       //但是我想在push里面用到这个方法
        return [[PresentTransition alloc] init]; // 返回push动画的类
    }else if(operation == UINavigationControllerOperationPop){
        return nil;
    }else {
        return nil;
    }
}

版权声明:本文为MemoryFate原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。