视图控制器转换(View Controller Transition) 俗称 关于转场动画
视图控制器中的视图显示在屏幕上有两种方式:最主要的方式是内嵌在容器控制器中,比如 UINavigationController,UITabBarController, UISplitController;由另外一个视图控制器显示它,这种方式通常被称为模态(Modal)显示。View Controller Transition 是什么?在 NavigationController 里 push 或 pop 一个 View Controller,在 TabBarController 中切换到其他 View Controller,以 Modal 方式显示另外一个 View Controller,这些都是 View Controller Transition。
UINavigationController 和 UITabBarController 这两个容器 VC 的根视图在屏幕上是不可见的(或者说是透明的),可见的只是内嵌在这两者中的子 VC 中的视图,转场是从子 VC 的视图转换到另外一个子 VC 的视图,其根视图并未参与转场;而 Modal 转场,以 presentation 为例,是从 presentingView 转换到 presentedView,根视图 presentingView 也就是 fromView 参与了转场。而且 NavigationController 和 TabBarController 转场中的 containerView 也并非这两者的根视图。
Modal 转场与两种容器 VC 的转场的另外一个不同是:Modal 转场结束后 presentingView 可能依然可见,UIModalPresentationPageSheet 模式就是这样。这种不同导致了 Modal 转场和容器 VC 的转场对 fromView 的处理差异:容器 VC 的转场结束后 fromView 会被主动移出视图结构,这是可预见的结果,我们也可以在转场结束前手动移除;而 Modal 转场中,presentation 结束后 presentingView(fromView) 并未主动被从视图结构中移除。准确来说,是 UIModalPresentationCustom 这种模式下的 Modal 转场结束时 fromView 并未从视图结构中移除;UIModalPresentationFullScreen 模式的 Modal 转场结束后 fromView 依然主动被从视图结构中移除了。这种差异导致在处理 dismissal 转场的时候很容易出现问题,没有意识到这个不同点的话出错时就会毫无头绪。下面来看看 dismissal 转场时的场景。
划重点
- UIModalPresentationFullScreen 模式:presentation 后,presentingView 被主动移出视图结构,在 dismissal 中 presentingView 是 toView 的角色,其将会重新加入 containerView 中,实际上,我们不主动将其加入,UIKit 也会这么做,前面的两种容器控制器的转场里不是这样处理的,不过这个差异基本没什么影响。
- UIModalPresentationCustom 模式:转场时 containerView 并不担任 presentingView 的父视图,后者由 UIKit 另行管理。在 presentation 后,fromView(presentingView) 未被移出视图结构,在 dismissal 中,注意不要像其他转场中那样将 toView(presentingView) 加入 containerView 中,否则本来可见的 presentingView 将会被移除出自身所处的视图结构消失不见。如果你在使用 Custom 模式时没有注意到这点,就很容易掉进这个陷阱而很难察觉问题所在。
小结:经过上面的尝试,建议是,不要干涉官方对 Modal 转场的处理,我们去适应它。在 Custom 模式下,由于 presentingView 不受 containerView 管理,在 dismissal 转场中不要像其他的转场那样将 toView(presentingView) 加入 containerView,否则 presentingView 将消失不见,而应用则也很可能假死;而在 presentation 转场中,切记不要手动将 fromView(presentingView) 移出其父视图。
模态的转场代理
|
|
模态转场动画及手势代理
|
|
手势处理是和Nav的处理是一样的
iOS 8 api:UIPresentationController
iOS 8 针对分辨率日益分裂的 iOS 设备带来了新的适应性布局方案,以往有些专为在 iPad 上设计的控制器也能在 iPhone 上使用了,一个大变化是在视图控制器的(模态)显示过程,包括转场过程,引入了UIPresentationController类,该类接管了 UIViewController 的显示过程,为其提供转场和视图管理支持。当 UIViewController 的modalPresentationStyle属性为.Custom时(不支持.FullScreen),我们有机会通过控制器的转场代理提供UIPresentationController的子类对 Modal 转场进行进一步的定制。官方对该类参与转场的流程和使用方法有非常详细的说明:Creating Custom Presentations。
UIPresentationController类主要给 Modal 转场带来了以下几点变化:
- 定制 presentedView 的外观:设定 presentedView 的尺寸以及在 containerView 中添加自定义视图并为这些视图添加动画;
- 可以选择是否移除 presentingView;
- 可以在不需要动画控制器的情况下单独工作;
- iOS 8 中的适应性布局。
以上变化中第1点 iOS 7 中也能做到,3和4是 iOS 8 带来的新特性,只有第2点才真正解决了 iOS 7 中的痛点。在 iOS 7 中定制外观时,动画控制器需要负责管理额外添加的的视图,UIPresentationController类将该功能剥离了出来独立负责,其提供了如下的方法参与转场,对转场过程实现了更加细致的控制,从命名便可以看出与动画控制器里的animateTransition:的关系:
|
|
没有 presentingView 是因为 Custom 模式下 presentingView 不受 containerView 管理,UIPresentationController类并没有改变这一点。iOS 8 扩充了转场环境协议,可以通过viewForKey:方便获取转场的视图,而该方法在 Modal 转场中获取的是presentedView()返回的视图。因此我们可以在子类中将 presentedView 包装在其他视图后重写该方法返回包装后的视图当做 presentedView 在动画控制器中使用。
参与角色都准备好了,但有个问题,无法直接访问动画控制器,不知道转场的持续时间,怎么与转场过程同步?这时候前面提到的用处甚少的转场协调器(Transition Coordinator)将在这里派上用场。该对象可通过 UIViewController 的transitionCoordinator()方法获取,这是 iOS 7 为自定义转场新增的 API,该方法只在控制器处于转场过程中才返回一个与当前转场有关的有效对象,其他时候返回 nil。
|
|
|
|
OverlayPresentationController类接手了 dimmingView 的工作后,需要回到上一节OverlayAnimationController里把涉及 dimmingView 的部分删除,然后在 presentedVC 的转场代理属性transitioningDelegate中提供该类实例就可以实现和上一节同样的效果。
介绍 UIViewControllerTransitionCoordinator