iOS中dismissViewController实现多级模态跳转

来源:互联网 发布:p2p种子搜索器mac版 编辑:程序博客网 时间:2024/06/03 16:41

最近项目中用到这样一个逻辑:三个视图控制器:A,B,C,A模态跳转到B,B在模态跳转到C,等C操作完后要直接跳转回A,那么问题就来了,我们平时一般用到的只是从谁跳转过来,就在跳转回去,那怎么实现多级返回呢?其实用到的根本的方法还是下面的方法:

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

flag:表示是否执行动画
competion:是一个block,用在dismiss操作完成后的回调

通常情况下presentViewController和dismissViewController是一组方法,我门用presentViewController模态跳转到下个页面,在用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方法。

想要实现直接从C跳转回A其实也简单,一个最简单的解决办法就是利用通知或者代理,在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视图控制器中不用添加任何代码,从而解决了耦合的问题。

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

1 0