转场的理解
转场过程中,作为容器的父 VC (UIViewController、UITabbarController 或 UINavigationController) 维护着多个子 VC,但在视图结构上,只保留一个子 VC 的视图。
转场的本质是下一场景(子 VC)的视图替换当前场景(子 VC)的视图以及相应的控制器(子 VC)的替换,表现为当前视图消失和下一视图出现,基于此进行动画。
具体可以看下图:
iOS中几种转场方式
Apple支持如下几种方式的自定义转场,并且提供转场代理来实现自定义转场。
UINavigationController 中 push 和 pop
1
UINavigationController 的 delegate 属性需遵守 <UINavigationControllerDelegate>
UITabBarController 中切换 Tab
1
UITabBarController 的 delegate 属性需遵守 <UITabBarControllerDelegate>
Modal 转场:presentation 和 dismissal,俗称视图控制器的模态显示和消失,仅限于 vc.modalPresentationStyle = .custom 或者 .fullScreen 时才能定制转场效果。
1
vc.transitioningDelegate 遵守 <UIViewControllerTransitioningDelegate>
UICollectionViewController 的布局转场:仅限于 UICollectionViewController 与 UINavigationController 结合的转场方式
如何实现自定义 Transition(转场)
1、自定义 Transition Delegate (转场代理) 提供给 控制器
2、在转场代理中提供转场动画的核心构件: 动画控制器 或者 交互控制器
动画控制器(Animation Controller):负责添加视图以及执行动画,遵守
UIViewControllerAnimatedTransitioning协议交互控制器(Interactive Controller):通过交互手段,通常是手势来驱动动画控制器实现的动画,使得用户能够控制整个过程;遵守
UIViewControllerInteractiveTransitioning,系统已经打包好现成的类UIPercentDrivenInteractiveTransition供我们使用。
3、在动画控制器、交互控制器中,根据转场环境(Transition Context)提供的数据,进行转场操作。
- 转场环境(Transition Context):遵守 UIViewControllerContextTransitioning 协议,由UIKit在转场开始前生成,为我们提供转场所需要的数据。
4、可以为转场代理提供 呈现控制器(UIPresentationController), 用于 UIViewController 的显示过程,为其提供转场和视图管理支持。
- 转场协调器(Transition Coordinator):遵守 UIViewControllerTransitionCoordinator 协议,为 UIPresentationController 中的动画的与转场进行同步,主要在 Modal 转场和交互转场取消时使用。
1 | // 获取转场协调器 |
一般来说,(push/presentation)转场结束后,UIKit 会把 fromView 从视图结构中移除,你也可以手动处理提前将 fromView 移除;(pop/dismissal)转场结束后,UIKit 会自动将 toView(presentingView) 加入 containerView。
iOS中三大转场代理中的方法不尽相同,但是它们返回的 动画控制器 和 交互控制器 所遵守的协议相同,这样便于我们封装。
Modal转场呈现的差别
从图中可以看出,Custom 模式下Modal转场后,fromeView 和 toView 在视图层次中还是存在可见的,在不同的 UITransitionView 中;而 Navigtion 转场结束后,fromeVC会被主动移出视图,只显示 toVC。
容器类 VC 的转场里 fromView 和 toView 是 containerView 的子层次的视图,而 Modal 转场里 presentingView 与 containerView 是同层次的视图,只有 presentedView 是 containerView 的子层次视图。
UITransitionView == containerView
它是UIView 的私有子类,用来呈现 presentedView.
可以在转场环境(Transition Context)中获取到它:
1 | let containerView = transitionContext.containerView |
或者 UIPresentationController 中的 containerView;
UIPresentationController
UIPresentationController是 iOS 8.0中新增的,它接管了 UIViewController 的显示过程,为其提供转场和视图管理支持。
- 定制 presentedView 的外观:设定 presentedView 的尺寸以及在 containerView 中添加自定义视图并为这些视图添加动画;
- 可以选择是否移除 presentingView;
- 可以在不需要动画控制器的情况下单独工作;
- iOS 8 中的适应性布局。
modal 转场的注意点
当 vc.modalPresentationStyle = .fullScreen 时
presentation 结束后 即将消失的场景的视图 presentingView(fromView) 会主动被UIKit从视图结构中移除,presentingView(fromView)为屏幕的 bounds;
dismissal 转场时,如果需要对 presentingView(fromView) 进行动画,则需要手动将其加入 containerView 并添加动画;转场结束后,UIKit 会自动将其恢复到原来的位置。
当 vc.modalPresentationStyle = .custom 时
presentation 结束后,presentingView(fromView) 不会被主动移出视图结构,与 即将出现的视图 presentedView(toView) 同时显示。
dismissal 结束后本来可见的 presentingView 将会随着 containerView 一起被移除,此时只显示 presentedView(toView)。
交互式转场
实现交互化的条件
给转场代理提供 交互控制器(遵守 UIViewControllerInteractiveTransitioning 协议),一般使用系统提供的 UIPercentDrivenInteractiveTransition 类。
交互控制器需要动画控制器和交互方式配合才能使用。最常见的交互方式是使用手势,或是其他事件,来驱动整个转场进程。
转场过程卡壳
如果给转场代理提供了 交互控制器,而发生转场并不是通过 交互方式 来驱动转场进程,比如点击返回按钮进行 push/pop 转场,这样会导致转场过程将一直处于开始阶段无法结束,应用界面也会失去响应,转场过程卡壳。
因此,仅在确实处于交互状态时才提供交互控制器。
问题的根源是交互控制的工作机制导致的,交互过程实际上是由转场环境对象 <UIViewControllerContextTransitioning> 来管理的,它提供了如下几个方法来控制转场的进度:
1 | func updateInteractiveTransition(_ percentComplete: CGFloat) |
在转场代理里提供了交互控制器后,转场开始时,该方法自动被 UIKit 调用对转场环境进行配置。
系统打包好的UIPercentDrivenInteractiveTransition中的控制转场进度的方法与转场环境对象提供的三个方法同名,实际上只是前者调用了后者的方法而已。
交互转场介入后, view 的 Appear 状态变得复杂
交互转场介入后,view 的Will系方法和Did系方法的执行顺序并不能得到保证
1 | func viewWillAppear(_ animated: Bool) |
那么,如何在转场过程中的任意阶段中断时取消不需要的效果?
学习的demo
1、iOS-ViewController-Transition:《iOS 视图控制器转场详解》配套 Demo,代码很全
2、Navigation Transition:简单的 push/pop 手势转场,博客中提到一些其它转场动画
3、CustomTransitions:Apple 提供的转场讲解代码,包含三种常用的转场
4、ShrinkingModalTransitions:Modal转场,类似ActionSheet,详细见其博客 使用 UIPercentDrivenInteractiveTransition 的讲解。
5、AnimationTrasitionDemo:点击 UITableCell 进行转场放大 cell 图片,详细见其博客 哈哈大p孩 讲解
开源库
TransitionTreasury:swift,2.1k
Hero:swift,18.5k,转场动画
Spring:swift,13.7k,简化Swift中的iOS动画
学习博客
github/seedante iOS 视图控制器转场详解
cnthinkcode
[MFMAL’s Blog](http://mfmal.tech/2016/08/10/使用`presentViewController`方法弹出模态控制器 并进行自定义操作(一))
ObjC 中国
iOS自定义过渡动画
Apple View Controller Programming Guide for iOS
Apple Creating Custom Presentations
Apple Customizing the Transition Animations