您的位置:首页 > 其它

dismissViewController实现多个模态视图跳转

2015-07-14 22:50 441 查看
最近实现的一个需求需要用到模态视图,所以少不了与dismissViewController方法打交道。本文主要讲一讲在使用dismissViewController方法过程中遇到的那些坑。

由于业务逻辑比较特殊,程序中需要在A视图控制器中present B视图控制器的视图,B视图控制器需要present C视图控制器中的视图。最后从C直接返回A。

dismissViewControllerAnimated方法并不难用,其原型为:

- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion;


1.flag的含义

flag用与判断知否执行动画(这个很关键,后面会细说)。completion是一个block,用于在dismiss之后执行回调。

我们知道,presentViewController和dismissViewController是一组方法,用于展现和关闭模态视图,而且整个过程的动画是可以完全自定义的。这里的自定义不是像网上有些代码那样,创建一个CATransition对象,设置一堆属性完事,依我愚见,这种只能选择几个固定的系统动画的方式和没有自定义动画没有任何区别!

真正的完全自定义是通过自定义一个动画类来完成。这段代码有点多,等有空总结一下。那么这里的flag对于自定义动画的影响在哪里呢?

答案是如果flag == NO,那么不会执行任何动画(当然也不会执行自定义动画),如果flag == YES,则有可能执行自定义动画,如果没有自定义动画则会执行系统默认动画。

2.dismissViewController在哪里执行

如果由A跳转到B,显然presentViewController方法应该在A里面执行,那么当我们希望关闭模态视图B时,dismissViewController在哪里执行呢?

标准答案是:在A视图控制器里执行。苹果的文档里有这么一段话:

The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

或者只要简单的记住一个原则既可:

谁污染,谁治理!

这是和pushViewController和popViewController那一组方法不同的地方。

不过在B视图中执行也是可以的,因为系统会自动优化,当B视图控制器没有present过其他视图控制器的时候,dismissViewController方法会自动交给B的presentingViewController执行,也就是A视图。

但是出于严谨性考虑,还是应该在A视图控制器中执行dismissViewController方法。

3.多个模态视图之间跳转

正如此前所说,需要从视图C直接返回视图A。在网上也看到一些解答,比如说利用通知让B视图控制器执行dismissViewController方法。其实这样是不行的,原因刚刚已经解释过,对于一个视图控制器X,它执行dismissViewController方法的时候将会关闭它present的模态视图,只有在它没有present过模态视图的时候,才会交给他的presentingViewController执行dismissViewController方法。所以这里如果交给B执行,和直接在C里面执行dismissViewController方法的效果是一样的。

显然一个最简单的解决办法就是利用通知或者代理,在A中执行dismissViewController方法。此时B和C视图控制器会发生什么变化呢?依然摘录一段苹果的文档做一下解释:

If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

也就是说,其实在present多个视图控制器的时候,系统维护了一个栈,以我们现在这个情况为例,从栈底到栈顶依次是A->B->C。当栈中某个位置的视图控制器执行dismissViewController方法的时候,栈中所有在它之上的视图控制器都会被dismiss,不同的是,栈顶的视图控制器将会以动画方式被dismiss,而中间的视图控制器只是简单的remove掉。

这里已经成功了一半,因为dismissViewController总是要在A方法中执行的。不过这样做会遇到代码耦合的问题。

所谓的代码耦合指的是,我们应该尽量避免代理或者通知的使用,比如这时候A视图控制器换成了A’ 那么A’在不修改任何代码的时候肯定是无法直接放在整个项目里面使用的。此时要么重新实现协议,要么监听通知。

其实解决办法很简单:

UIViewController *rootVC = self.presentingViewController;
while (rootVC.presentingViewController) {
    rootVC = rootVC.presentingViewController;
}
[rootVC dismissViewControllerAnimated:YES completion:nil];


在循环中连续获取presentingViewController,于是最终可以得到根视图控制器,这里就是A,不过这使得A视图控制器中不用添加任何代码,从而解决了耦合的问题。

这样写的另一个好处是,不管是多少个视图控制器之间的跳转,都可以很方便的完成。

4.自定义动画调用dismissViewController的坑

自定义动画是个坑,通过自定义动画,可以非常方便的定制一些功能,不过使用过程中也必须非常小心。

在之前的文档中可以看到,在A中执行dismissViewController方法,B会被直接remove掉,这个毫无问题。但是如果A present B和dismiss B用的是自定义动画,那么即使此时并不执行B的dismiss动画,依然会有问题。目前尚不清楚确切原因。但是务必要注意的是,一定要按照标准来写自定义动画,而不是简单的实现了动画就完事。

关于标准的自定义页面跳转动画,https://developer.apple.com/videos/wwdc/2013/ 给出了一些指导,有兴趣的话找一找Controllers“Custom Transitions Using View Controllers”即可。

等有空会单独写一篇关于自定义视图跳转动画的博客。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: