项目中经常有这样的需求:
A present弹出 B
B present弹出 C
C dismiss回到 A
对数模态弹出的控制器不像push的那样,有系统的导航栏统一管理。为实现这一目的,我仔细研究了官方文档,发现了这么一句话
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.
也就是说,谁污染谁治理,A present弹出 B,本应该由A dismiss B然后回到再A。
我们一般都是直接用B dismiss,这是因为系统会自动优化,当B视图控制器没有present弹出过其他视图控制器的时候,dismissViewController方法会自动交给B的presentingViewController执行,也就是A视图。
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掉。
所以想要实现直接从C跳转回A也简单,就是直接在A中执行dismissViewController方法,此时B也会自动销毁,同pop方法类似。但是我们需要在每一个弹出的控制器中都设置监听,来实现谁污染谁治理这个规则。这样就太乱了。
好在苹果应该是想到这一点,为我们提供了两个属性,分别是presentingViewController和presentedViewController。
A弹出B 则A是B的presentingViewController,
B是A的presentedViewController。如果B还弹出了C,那么B同时也是C的presentingViewController。
说了这么多,也该上代码了,同上pop一样,我也是写了一个Extension,复制粘贴即可轻松实现dismiss多层。不过呢我没有封装完成后的回调,需要的同学可以自己加上。
.h 文件
@interface UIViewController (Extension)
/** 在主线程执行操作*/
- (void)performSelectorOnMainThread:(void(^)(void))block;
/** 退出 presentViewController count:次数*/
- (void)dismissViewControllerWithCount:(NSInteger)count animated:(BOOL)animated;
/** 退出 presentViewController 到指定的控制器*/
- (void)dismissToViewControllerWithClassName:(NSString *)className animated:(BOOL)animated;
@end
.m 文件
@implementation UIViewController (Extension)
/** 在主线程执行操作*/
- (void)performSelectorOnMainThread:(void(^)(void))block{
if ([[NSThread currentThread] isMainThread]) {
block();
}else{
dispatch_sync(dispatch_get_main_queue(), ^{
block();
});
}
}
/** 退出 presentViewController count:次数*/
- (void)dismissViewControllerWithCount:(NSInteger)count animated:(BOOL)animated{
count--;
// 不是自己,并且自己弹出过VC, 递归交给自己弹出的VC处理
if (count>0 && self.presentingViewController) {
[self.presentingViewController dismissViewControllerWithCount:count animated:animated];
}
else{
[self dismissViewControllerAnimated:animated completion:nil];
}
}
/** 退出 presentViewController 到指定的控制器*/
- (void)dismissToViewControllerWithClassName:(NSString *)className animated:(BOOL)animated{
// 不是自己,并且自己弹出过VC, 递归交给自己弹出的VC处理
if (![self.class isKindOfClass:NSClassFromString(className)] && self.presentingViewController) {
[self.presentingViewController dismissToViewControllerWithClassName:className animated:animated];
}else{
[self dismissViewControllerAnimated:animated completion:nil];
}
}
@end