前言
之前一直对容器转场这一块很模糊,因为这里面涉及到好几个协议的实现,而且名字比较长.读来总是不想仔细去看协议的名称.对于视图切换经常都是使用iOS系统内部自带的present-dismiss动画,或者导航栏的push和pop动画.虽然这部分在我们公司目前的app内也是用的比较多的,但是我感觉在注重用户体验后,这部分也是会进一步优化的.正好前段时间看到了一些关于场景切换的博客,就正好学习一下,并且写一个小demo记录下.方便后面前来查找.
iOS转场动画分类
iOS页面转场主要分为三种,uiviewcontroller页面转场,uinavigationviewcontroller页面转场,uitabbarcontroller页面转场,本文主要介绍前两种转场方式.其中对于uiviewcontroller的页面转场是通过present和dismiss的方式进行的页面切换.uinavigationcontroller的页面转场是通过navigtioncontroller管理的子vc的栈进行的push和pop切换.对于系统提供的页面切换,可以使用系统提供的API进行.
1 | /// 模态弹出的方法 |
上面的后两个方法是通过导航栏控制调用的,如果要使用导航栏动画,需要设置当前页面容器的根视图是导航栏控制器.uinavigationcontroller里面有很多属性都是日常开发中经常会用到的,可以结合这篇设计透明导航栏的文章参考下.
如果想要自定义场景切换动画,需要调用iOS提供的一些视图动画协议.我在demo中实现了如下几种效果的页面场景切换
present-dismiss转场
UIViewControllerContextTransitioning && UIViewControllerAnimatedTransitioning
在开始前先熟悉下几个关于自定义转场的协议类
- UIViewControllerContextTransitioning ,这个协议提供了视图切换上下文,在开发过程中一般不需要直接继承这个协议.在使用vc进行场景切换时,可以使用这个协议对象取得动画前后的视图容器.只看这个协议会觉得模糊不清,结合下一个协议一起来看会比较清楚.
- UIViewControllerAnimatedTransitioning ,这个协议类中定义了动画执行的主要方法.其中包含了两个动画相关的主要方法
1 | /// 返回动画执行的时长 |
可以看到参数是实现了UIViewControllerContextTransitioning协议的参数.通过这个参数可以获取到视图和容器相关的一些参数
1 | /// 获取转换前后的视图容器 |
有了以上的动画方法,取到动画切换前后的视图,就可以定义一些简单的动画效果.
1 | - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { |
上面自定义动画的过程中
- 使用context取到目的视图的容器,(viewControllerForKey中的key可以取值fromVC和toVC)
- 确定动画视图的初始位置,上图中展示了动画从下到上(1)和从左上到右下(0)的效果.
- 将视图添加到containerView中准备进行动画.
- 执行动画
- 标记动画是否完成
UIViewControllerTransitioningDelegate
有了动画实现类,只需要找到调用方即可,比如A容器推到B容器,那就可以考虑让A容器在调用动画实现.因此考虑让A容器作为代理实现动画调用过程的协议方法.
1 | /// present动画调用方法, |
在present动画执行时返回前面自定义的动画
1 | - (void) viewDidLoad { |
自定义的通过手势滑动关闭页面的动画可以在文末参考demo实现. 通过上面的方法就可以实现一个自定义的页面场景切换动画.整个过程大致如下.
- 自定一个类继承 UIViewControllerAnimatedTransitioning 协议,并实现其中的自定义动画方法和动画执行时长方法.
- 在需要执行动画的VC中继承 UIViewControllerTransitioningDelegate,设置要执行动画的代理尾当前容器vc.transitioningDelegate = self;
- 实现UIViewControllerTransitioningDelegate中的协议方法,在协议方法中返回自定义的动画效果
- 调用控制器的presentViewController方法.系统在调用和这个方法后,如果有自定义动画,会走自定义动画,没有的话就走系统的present动画
如果需要实现一些更加炫酷的动画效果,可以自行了解iOS动画机制.
push-pop动画
push和pop的自定义动画实现过程非常类似.也有三个相关的协议类,只是最后在调用动画实现api时需要实现UINavigationControllerDelegate这个协议中的动画方法.需要注意的是,使用导航类动画的话需要保证当前所在的容器是包含导航栏的.因为在进行push-pop动画时其本质都是通过导航栏管理一个容器堆栈.下面实现一个自定的导航栏动画效果.
- 自定义一个动画类,继承UIViewControllerAnimatedTransitioning,重新协议方法.
1 | - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { |
在需要实现动画的控制器中实现 UINavigationControllerDelegate,并将导航控制器的代理设置为容器本身.
1
2/// 如果在这一步将代理设置为被push的容器,那么自定义动画只会在pop返回时生效. pushVc.navigationController.delegate = pushVc;
self.navigationController.delegate = self;实现UINavigationControllerDelegate协议中的动画调用方法.返回自定义动画
1 | - (void)viewDidLoad { |
和present动画另一个不同的点是,此处的动画是push和pop都会调用的.如果想设置push和pop不同的效果可以判断何时是push,何时是pop来进行区分.自定义的push动画效果过程大致如下:
- 自定一个类继承 UIViewControllerAnimatedTransitioning 协议,并实现其中的自定义动画方法和动画执行时长方法.
- 在需要执行动画的VC中继承 UINavigationControllerDelegate,设置要执行动画的代理尾当前容器self.navigationController.delegate = self;
- 实现UINavigationControllerDelegate中的协议方法,在协议方法中返回自定义的动画效果
- 调用navigationcontroller的pushViewController:方法,触发动画,如果有自定义动画则走自定义动画,否则走系统自带动画效果.
参考文章
iOS导航控制器——UINavigationController使用详解
demo地址
demo里面比较杂,放的东西比较多.
之后了解下如何截gif图,尝试在博客中也添加一些gif图增加效果.