iOS DeviOS 控制器转场iOS转发

iOS 中使用ViewController控制转场的各种方法

2016-03-28  本文已影响7513人  ac3

iOS中,使用ViewController进行页面跳转的方法有很多,之前总是想到哪用到哪,最近在review项目的code时候,抽空整理了一下,给自己理顺思路。
由于iOS中MVC的概念,view很多时候和ViewController是关联在一起的,这就决定了View的展示可以通过直接操作View和间接操作ViewController两种情况。第一种的核心其实就是addSubView,而第二种的核心,其实是ViewController之间的层级关系。本文主要针对相对复杂的第二种情况。
一上来就说结果。目前我整理出来的,通过操作ViewController进行不同的View跳转的方法一共有以下2大类9种:

1. Sibling Views Present

2. Container Views Present

这里用一个demo来复习下上述9种种除系统自定义Controller跳转和完全自定义transition两种之外的其他7个场景。
首先看下demo的结构:一共有Main, A, B, C 4个View。其中Main是主入口。
1)Main到A, Main到B演示第一大类的5种方法;
2)B到A演示第(7)种
3)B到A和C演示第(8)种



在描述下述几个方法之前,首先需要理解一些基本概念:
展示一个VC会在原VC和新VC之间建立一个关联,其中原始的那个VC叫做presenting view controller,被展示出来的那个新的VC叫做presented view controller。这种关联形成了VC之间的层级关系并且会一直持续到presented VC被dismiss掉。


(1)直接使用Storyboard

StoryboardSegue.gif

(2)Storyboard和Code混合

上述方法(1)必须从确定的presenting VC的某个物件上拖拽Segue到指定的presented VC上。如果不想指定特定的源物件,或者你的Segue的发生地点和时间不确定等等,你可以直接使用performSegueWithIdentifier:sender: 触发Segue:

StoryboardCode.gif

(3)纯Code手工Segue

如果Storyboard预定义的几种segue不能满足你的需求,你可以用code定制化一个Segue:

Demo:
首先,我们创建一个自定义Segue:MySegue。这里简单起见,perform方法只是调用后文提到的presentViewController。新的VC被present之后将背景色改成紫色:

MySegue.png

方法(1),直接在Storyboard里选择Custom Segue:


mySegue.gif

你也可以直接初始化一个MySegue,然后手工调用perform:


CustomSegueCode.png

最后,使用dismiss方法退回presenting VC:

screenshots3.gif screenShot3.gif

关于Presentation Style,Presentation Context 和 transition Style

在介绍(4)(5)之前先了解下这几个概念。iOS设备的屏幕在方向上分为vertical, horizontal两种,每个方向的大小上又分为 compact,regular,Any三种,一共组合起来有3*3=9种不同的适配模式(叫做size class)。如果你没有特殊指定在特定的size class下使用哪种特殊的presentation style,presenting VC会替你选择最优的方式并且自动给你调整layout constrains。

Presentation Style

Presentation Style是iOS提供的多种默认的展示方式,分为:

而在horizontally compact 环境下(主要针对iPhone),无论你选择什么参数,最终都会使用UIModalPresentationFullScreen而覆盖下层整个屏幕的内容。

注意1:使用UIModalPresentationFullScreen style时,UIKit通常会在animation结束后remove掉下层被遮挡的View 。如果不希望这样,比如当展示一个透明的View的时候希望能显示下层的内容,就可以使用 UIModalPresentationOverFullScreen 值。

注意2:在Full Screen下,最终的presenting VC不一定是你实际调用的presenting VC。UIKit会回溯你的VC hierachy来找到最近的一个全屏的VC来控制presentation过程,如果找不到,最终会选择window的root VC来做presenting VC。在后文第(8)种方法之后我们的Demo将会来验证这个事情。

而在horizontally Compact下(主要针对iPhone),Popover样式会自动适配到UIModalPresentationOverFullScreen Style。这种情况下,由于会覆盖整个屏幕,你需要自己设计一种退出的方法,可能是加一个退出按钮,或者是把popover迁入到另一个单独的container VC中等等。

在horizontally compact环境下,current context styles将自适应到UIModalPresentationFullScreen。

同理,你可以使用UIModalPresentationOverCurrentContext来阻止UIKit自动移除下层的View。

Transition Style

Transition Style决定了显示presented VC的动画样式。UIKit内置了很多预定义的动画,这些动画就取决于你在presented VC中设置的modalTransitionStyle属性。比如下图所示的 UIModalTransitionStyleCoverVertical 值决定的动画:

TransitionStyle.png

你也可以使用(9)中描述的方法来自定义更加复杂的显示动画。


(4)showViewController:sender: / showDetailViewController:sender:

这是展示一个新的VC最简单也是最有效的方法,也是Apple推荐的方法。原因是这些方法能够让presented VC自由的自动选择最佳的展示方式来展示presenting VC,你自己不用操心presenting VC和presented VC是在一个Navigation Controller或者是在一个split-view Controller里,你也不用关心具体的动画流程。一切都交给UIKit自己去完成。使用方法:

showViewControlle:sender: 和showDetailViewController:sender:之间的区别是前者默认替换的是Primary context VC,而后者是替换Secondary context VC。你可以重写这两个方法来自己显示presented
VC,但是应当确保它们各自操作的context和系统默认定义的规则一致。

Demo Code:使用showDetailViewController来展示View B:


showDetailViewController.png showDetailViewController.gif

(5)presentViewController:animated:completion:

这是除(4)之外另一个常用的简单方法,相比之下,它的优势是可以控制是否显示动画开关和completion block,能够让你实现更多的自定义功能,但是它总是modally显示新的VC。一般情况下,horizontally compact(iphone) 环境下,推荐使用这个方法。

demo示例:用presentViewController来展示A,并且在展示结束时使得A的背景色改为紫色:

屏幕快照 2016-03-28 下午3.44.27.png screenShot5.gif

(6)系统自定义UINavigationController, UITabBarController, UISplitController

这几个是iOS提供的内置Container VC,这个不在此多说,可以参考Apple的开发文档。

在早起版本的iOS里,这是唯一可以使用的Container VC,绝大多数情况下已经够用了。但是有些情况下这些预定义Container并不够用,这种情况下,增加一个新的页面只能通过addSubView的方式,从而造成一个VC可能会接管大量的sub view,这不但会造成VC的臃肿不方便代码的维护,也会造成层级结构的不清晰。所以从iOS7之后,Apple提供了可以自定义Container VC的方式,于是就有了下面的(7)(8)两种新的方法。

(7) addChildViewController: / removeFromParentViewController

严格意义上来说,这其实并不是直接进行View之间切换的方法,因为此方法需要UIView的addSubView:方法的配合。但是这种方法的核心思想却是addSubView所没有的,那就是在Container VC中创建的父子关系。

使用addChildViewController: 方法时,需要特别注意调用次序:

对应的,当删除Child VC时:

实际上,willMoveToParentViewController:和didMoveToParentViewController:应该成对出现,只是UIKit自动替你完成了部分工作,调用addChildViewController:时已经调用了前者,调用removeFromParentViewController:时已经替你调用了后者。

Demo示例:在View B上调用addChildViewController和addSubView展示出View A,并且调整A的大小为150*150:


addChildViewController.png

在View A上增加返回View B的按钮,调用removeFromParentViewController和removeFromSuperView:


removeFromParentViewController.png

Demo演示:

screenShot7.gif

(8)transitionFromViewController:toViewController:duration:options:animations:completion:

这个方法其实是上述(7)在多个Child VC场景下的进阶版,UIKit替你考虑到了一个最常见的场景,就是一个Container VC需要在多个Child VC之间进行切换,比如Navigation Controller需要不断替换自己的几个子View界面。这个时候你就可以直接调用此方法。

注意,调用这个方法有一个明确的前提:FromViewController和toViewController都必须是调用者(Presenting VC)得Child VC,如果没有提前建立父子关系,系统运行时会crash。所以,在调用此方法前必须将fromViewController移除Parent VC,将toViewController加进Parent VC,同时在对应的时序点调用各自对应的willMoveToParentViewController:和didMoveToParentViewController:方法。

Demo示例:首先在View B上通过方法(7)添加Child VC展示出View A,然后通过transition方法替换View A到View C。


屏幕快照 2016-03-28 下午4.45.38.png transitionFromAToC.gif

以上8种再加上单独调用UIView的addSubView:, transitionWithView:duration:options:animations:completion:
, 和transitionFromView:toView:duration:options:completion:已经基本上能够满足绝大多数页面跳转需求了。

上文有提到,presentation的最终执行者(也就是presenting VC)并不一定就是你调用presentation的VC,这点我们在Demo中来看一下:
我们在View A, View C中添加一个Label,在显示出View的时候,将当前的presenting VC的class名字显示在label上,同时为了方便看清楚当前的View是谁,增加一个nameLabel显示自己的类名:

nameLabel.png

接着我们用方法(1)通过Storyboard Segue在View A上增加展示出View C的按钮,然后我再来看下A和C上在不同的场景下显示出来的presenting VC是什么:

screenShot9.gif

可以看到,C上显示的presenting VC不是A,而是B,这是因为调用presentViewController的A此时并不是全屏的VC,它不能modally handle presentation,必须通过它的父级B才能完成;

- 另一种情况,从main VC全屏present到A,然后再通过Add展示

screenShot9.gif

这时就可以看到在A和C之间互相展示时,presenting VC各自都是对方。

这里留一个思考题:在第一种情况下,为什么从B展示A时,A的presenting VC为main VC(就是名为ViewController),而不是ViewController B ?(注意B是由Main VC 调用showDetailViewController 展示出来)

(9)完全自定义转场

最后一种高阶用法,将给你最大的自由度去定制化跳转过程,但是相对而言也是最复杂的一种。核心是将上述的转场过程中的控制单元全部暴露出来让你一个个的定制化,包括:

OK,希望能帮助到阅读到此文的读者快速掌握iOS中使用VC进行不同页面之间跳转显示的大部分方法。

上一篇 下一篇

猜你喜欢

热点阅读