转场的理解
转场过程中,作为容器的父 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