转场动画
- 我们需要自定义一个遵循的协议的动画过渡管理对象,并实现两个必须实现的方法.
//返回动画事件
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//所有的过渡动画事务都在这个方法里面完成
- (void)animateTransition:(id)transitionContext;
-
我们还需要自定义一个继承于UIPercentDrivenInteractiveTransition的手势过渡管理对象,我把它成为百分比手势过渡管理对象,因为动画的过程是通过百分比控制.
-
成为相应的代理,实现相应的代理方法,返回我们前两步自定义的对象就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 版权协议,转载请附上原文出处链接和本声明。