场景
假设现在有这样一个场景,我有一个根导航控制器RootNavigationController
,它的根视图控制器ViewController
有一个子控制器UINavigationController
,这个导航控制器的根控制器是FirstViewController
。
在FirstViewController
中,添加一个方法使用presentModelViewController
的方式跳转到另一个根控制器为NextViewController
的UINavigationController
如果NextViewController
中获取它的presentingViewController
属性,结果得到的是RootNavigationController
2016-12-18 10:34:09.548 PresentVCDemo[27202:22455163] viewDidLoad <RootNavigationController: 0x7ff29f83ba00>
2016-12-18 10:34:09.549 PresentVCDemo[27202:22455163] viewDidLoad <ViewController: 0x7ff29de05870>
2016-12-18 10:34:09.572 PresentVCDemo[27202:22455163] viewDidLoad <UINavigationController: 0x7ff29f022a00>
2016-12-18 10:34:09.573 PresentVCDemo[27202:22455163] viewDidLoad <FirstViewController: 0x7ff29dc0c680>
2016-12-18 10:34:52.785 PresentVCDemo[27202:22455163] Presenting Controller : <RootNavigationController: 0x7ff29f83ba00>
原因
苹果官方的�ViewController Programming Guide中关于Presenting a View Controller的部分是这样说的
The view controller that calls the presentViewController:animated:completion: method may not be the one that actually performs the modal presentation. The presentation style determines how that view controller is to be presented, including the characteristics required of the presenting view controller.
也就是说,在调用presentViewController:animated:completion:
方法时,真正作为跳转的容器并不一定是调用这个方法的view controller,而是取决于modalPresentationStyle
。例如,一个全屏的跳转必须由一个全屏的view controller来完成。如果当前的控制器不能满足,那么系统会自动沿着视图控制器的层级向上查找。
在我们的层级中,只有两个UINavigationController
是全屏的,因此,这两个控制器都有可能成为最终跳转的容器。可是为什么最终是RootNavigationController
完成了这次跳转而不是FirstViewController
的导航控制器呢?
在The View Controller Hierachy关于Presented View Controller中找到了这样一句话:
When you present a view controller, UIKit looks for a view controller that provides a suitable context for the presentation. In many cases, UIKit chooses the nearest container view controller but it might also choose the window’s root view controller.
因此,在一开始提到的场景中,UIKit帮我们选择了UIWindow的根视图控制器,而不是FirstViewController
的导航控制器。
解决
在某些特定的业务需求中,我们需要利用presentingViewController
拿到FirstViewController
的导航控制器。
解决这个需求,我们需要用到一对属性
@property(nonatomic,assign) BOOL definesPresentationContext;
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle;
modalPresentationStyle
属性决定了将要present的控制器以何种方式展现,默认值为UIModalTransitionStyleCoverVertical
definesPresentationContext
就有点神奇了,他的注释文档是这么写的
Determines which parent view controller's view should be presented over for presentations of type UIModalPresentationCurrentContext. If no ancestor view controller has this flag set, then the presenter will be the root view controller.
简单来说,如果把一个控制器的definesPresentationContext
属性设置为YES,那么在需要进行UIModalPresentationCurrentContext
类型的跳转的时候,UIKit会使用视图层级内的这个控制器来进行跳转。
在FirstViewController
中加入下面的代码
[self.navigationController setDefinesPresentationContext:YES];
并在跳转的时候设置目标控制器的modalPresentationStyle
[nav setModalPresentationStyle:UIModalPresentationCurrentContext];
[self presentViewController:nav animated:YES completion:nil];
接下来看一下Log日志
2016-12-18 11:59:20.041 PresentVCDemo[32336:22885768] viewDidLoad <RootNavigationController: 0x7fd6f4810600>
2016-12-18 11:59:20.042 PresentVCDemo[32336:22885768] viewDidLoad <ViewController: 0x7fd6f2c04110>
2016-12-18 11:59:20.052 PresentVCDemo[32336:22885768] viewDidLoad <UINavigationController: 0x7fd6f381ce00>
2016-12-18 11:59:20.052 PresentVCDemo[32336:22885768] viewDidLoad <FirstViewController: 0x7fd6f2f090c0>
2016-12-18 11:59:20.920 PresentVCDemo[32336:22885768] Presenting Controller : <UINavigationController: 0x7fd6f381ce00>
大功告成!现在presentingViewController
能够获取到我们期望的对象了。