文章的由来:
偶然在一次想要判断当前控制器是通过present模态推出还是通过push入栈显示的时候注意到了presentingViewController和presentedViewController两个属性,即:如果presentingViewController不为空,则当前控制器是通过present模态推出的方式显示的,那么相应的返回方式应该是dismiss而不是pop,之后又在头文件看到还有一个presentedViewController,就研究了下这两个属性的具体意义
文档中的presentingViewController和presentedViewController
presentingViewController
The view controller that presented this view controller.
When you present a view controller modally (either explicitly or implicitly) using the presentViewController:animated:completion: method, the view controller that was presented has this property set to the view controller that presented it. If the view controller was not presented modally, but one of its ancestors was, this property contains the view controller that presented the ancestor. If neither the current view controller or any of its ancestors were presented modally, the value in this property is nil.
大意是:呈现此视图控制器的视图控制器。
当您使用presentViewController:animated:completion: 方法(显式或隐式地)呈现一个视图控制器时,呈现的视图控制器将这个属性设置为呈现它的视图控制器。如果视图控制器不是以模态【present】呈现的,但是它的上一级是,这个属性包含呈现上一级的视图控制器。如果当前视图控制器或它的任何上一级都没有以模态方式显示,则此属性中的值为nil。
举例说明:
有五个控制器 ABCDE,应用启动先显示根控制器A,之后A通过present方式推出带导航栏的B,B再通过push的方式推出C,C再通过push的方式推出D,D再通过push的方式推出E,那么E的 presentingViewController 就是 B
也就是presentingViewController即为当前控制器的上级控制器【即父级控制器】,并且是通过present方式显示的父级,上面例子中只有B是通过present方式推出的,且B是CDE的父级,那么 D 的presentingViewController也将是B,大家可以自己理解一下
同理:如果还是有五个控制器 ABCDE,应用启动先显示根控制器A,之后全部是通过present方式依次推出 B、C、D、E,那么,这个时候E的presentingViewController就是D了,D的presentingViewController就是C
哈哈哈,会不会说的有点多,其实自己看看概念慢慢想就会想通的,后面会再介绍一下这个属性有什么用,会用在哪种场景
presentedViewController
The view controller that is presented by this view controller, or one of its ancestors in the view controller hierarchy.
When you present a view controller modally (either explicitly or implicitly) using the presentViewController:animated:completion: method, the view controller that called the method has this property set to the view controller that it presented. If the current view controller did not present another view controller modally, the value in this property is nil.
大意是:视图控制器由该视图控制器呈现,或其在视图控制器层次结构中的祖先之一。
当您使用presentViewController:animated:completion: method方法以模态(显式或隐式)方式呈现一个视图控制器时,调用该方法的视图控制器将这个属性设置为它所呈现的视图控制器。如果当前视图控制器没有以模式显示另一个视图控制器,则此属性中的值为nil。
举例说明:
有两个控制器AB,A通过present模态推出B,那么A的presentedViewController就是B,也就是你通过present模态推出了谁,你的presentedViewController就是谁,这样就好理解了,哈哈,这个属性就是这么简单,当然,如果没有模态推出任何控制器,那么属性值就是nil
嗯,剩下一个dismissViewControllerAnimated这个方法解释完,就不会愧对我的标题了🙃,这个方法有两种使用方式哦,跟着我继续看吧......
还是先看文档解释
Dismisses the view controller that was presented modally by the view controller.
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
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.
If you want to retain a reference to the view controller'��s presented view controller, get the value in the presentedViewControllerproperty before calling this method.
The completion handler is called after the viewDidDisappear: method is called on the presented view controller.
大意是【这里我就不用翻译软件翻译了,翻译的都不够准确,直接是我自己的理解,大家认为有什么不准确的欢迎指出】:
这个方法是用来将某一个通过present模态显示的视图控制器取消掉模态显示
如果连续模态推出了多个控制器,并在最上一级控制器执行了该方法,则会从最上一级控制器开始,后面所有通过present显示的控制器都会被dismiss掉,且这些控制器在被dismiss的时候仍然会执行viewWillAppear以及viewDidDisappear等相关视图显示或消失的方法,但是消失的过程却是一瞬间的,不论中间多少个控制器,你看到的效果就像只dismiss了一个控制器一样
举例说明:有5个控制器ABCDE,应用启动后显示根控制器A,之后Apresent出B,B present出C ,,,,一直到E,那么等到E完全显示的这个时候,如果A执行了dismiss,你将会看到B消失的动画,同时,CDE也会被dismiss,实际上dismiss的顺序和你present的顺序一致,即从E->D->C->B,然而只有B才会有dismiss的推出动画,想必是苹果对这一部分做了优化
进入正题进入正题进入正题进入正题进入正题进入正题进入正题进入正题
前面说到了dismissViewControllerAnimated有两种用法,其实大家从文档中就应该发现这个方法是用来取消掉某一个控制器的模态显示的,那么是谁来取消掉的呢?一般大家都是这么写的吧,如下:
[self dismissViewControllerAnimated:YES completion:nil];
这么写的含义是:让系统【即UIKIT框架内部处理】将当前控制器dismiss掉
但实际上,我们应该这么写:
if (self.presentingViewController) { [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } else { [self dismissViewControllerAnimated:YES completion:nil]; }
这么写的含义是:当前显示的控制器是由谁模态推出的就由谁来dismiss掉,如果是最上一级的就交给系统来dismiss,因为最上一层没有presentingViewController,为啥没有,大家再回去对照presentingViewController的概念,哈哈哈哈,看看B的presentingViewController是不是没有
最后就是应用场景了:
如果多个控制器都通过 present 的方式跳转呢?比如从A跳转到B,从B跳转到C,从C跳转到D,如何由D直接返回到A呢?可以通过 presentingViewController 一直找到A控制器,然后调用A控制器的 dismissViewControllerAnimated 方法。方法如下:
UIViewController *controller = self; while(controller.presentingViewController != nil){ controller = controller.presentingViewController; } [controller dismissViewControllerAnimated:YES completion:nil];
PS:如果不是想直接返回到A控制器,比如想回到B控制器,while循环的终止条件可以通过控制器的类来判断。
OK,打完收工,装逼要有限度🙃