视图控制器UIViewController(含其子类)——一个iOS开发绕不开的东东,网上对此的介绍不胜枚举,在此不累述它的功能和使用方法,只想谈谈不同视图控制器间的关系。
视图控制器的存在体现了MVC的设计理念,但不同于一般的MVC模式,视图控制器有些霸道,正如其名,它将视图变成了自身的一部分,不仅仅负责视图与数据的对接,也视图对象的创建。这似乎与web开发框架使用的MVC模式稍稍不同。不管它,言归正传。
首先我们要搞清楚视图控制器间的两种关系——父子关系和显示与被显示关系。
父子关系
在UIViewController的子类对象中,UINavigationController对象、UITabBarController对象和UISplitViewController对象比较特殊,它们是包含视图控制器的视图控制器,我们称之为视图控制器容器。它们都有一个类型为数组的viewControllers属性,用以保存一组视图控制器。作为UIViewController的子类对象,它们也有view属性,并且将viewControllers中的对象的view加入自己的view中。
当我们使用上述视图控制器容器时,视图控制器容器与其viewControllers中的对象间就会产生父子关系。正如你知道的,视图控制器容器是父亲,viewControllers中的对象是孩子。
处在同一个父子关系下的视图控制器形成一个家族。比如,UITabBarController对象跟它viewControllers中的对象就属于一个家族。家族可以有多个层级。例如,一个UITabBarController对象可以包含若干个UINavigationController对象,每个UINavigationController对象又可以包含若干个UIViewController对象;再如,一个UINavigationController可以包含一个UITabBarController对象,一个UITabBarController对象又可以包含若干个UIViewController对象(此模式不常见,但在实际开发中也会遇到)。在上述两种情形下,UINavigationController对象、UITabBarController对象和UIViewController对象同属一个家族。见下图示例1、示例2:
任何容器对象均可通过viewControllers访问它的子对象,子对象也均可通过UIViewController对象的navigationController、tabBarController、splitViewController以及parentController属性来访问它的容器对象。注意,前三个属性与最后一个属性不同,当某个视图控制器调用navigationController、tabBarController或者splitViewController属性时,会在本家族内向上查找,直到找到类型匹配的视图控制器容器,若没找到,则其对应属性为nil。而调用parentController属性时会查找最近的那个容器对象,所以parentController所指向的对象跟navigationController、tabBarController和splitViewController三个中的某一个指向相同。注意,UIViewController有个addChildViewController:方法,如果一个视图控制器(C)通过该方法添加了另一个视图控制器(D),那么D的parentController便指向C。这种情况不是我们本文讨论的范围。
显示与被被显示关系
如果某个视图控制器(A)以模态形式显示另一个视图控制器(B),那么A和B间的关系就是显示与被显示关系。此种关系下B的视图会覆盖A的视图,这与父子关系中子控制器视图只会显示在父控制器视图内不同。此外,任何UIViewController对象或者其子类对象都可以以模态形式显示另一个视图控制器;同时,任何UIViewController对象或者其子类对象都可以被另一个视图控制器以模态形式显示。在显示与被显示关系中,A的presentedViewController属性指向B,而B的presentingViewController属性指向A。如下图所示:
显示与被显示关系中的家族关系
在显示与被显示关系中,显示视图控制器与被显示视图控制器不在一个家族中。被显示的视图将形成自己的家族,这个家族中有一个或多个视图控制器。显示与被显示关系中的家族关系如下图所示,图中有两个家族。
结合上面的示例图,我们可以得出以下总结:
1、父子关系属性tabBarController、navigationController、splitViewController(示例中没有)、parentViewController所指对象都在本家族内。因此家族2中的视图控制器调用tabBarController属性返回的是nil,而不是家族1中的UITabBarController对象。同样地,家族2中的视图控制器调用navigationController返回的是家族2中的UINavigationController对象,而不是家族1中的。
2、某个视图控制器以模态形式被显示时,负责显示该视图控制器的是相关家族中最老的或者说最根部的那个。所以无论家族1中的任何一个视图控制器模态弹出家族2,家族2中的视图控制器的presentingViewController属性都指向家族1中的UITabBarController对象;而家族1中的视图控制器的presentedViewController都指向家族2中的UINavigationController对象。
关于模态形式显示视图控制器再补充点iPad上的特有行为。
之前我们说过,以模态形式显示的视图控制器其视图会完全覆盖负责显示它的视图控制器的视图,但在iPad中我们可以改变这种情况。暂不讨论如何改变,先来说说UIViewController的一个属性definesPresentationContext,它决定了显示权是否传递给父视图控制器。definesPresentationContext为NO时显示权会传递给父视图控制器,并查询父视图控制器的definesPresentationContext是否为NO,如果为NO,继续在本家族内往上传递,直到最根部视图控制器。如果某个视图控制器definesPresentationContext为YES,那么它就不会将显示权传个父视图控制器,而由自己负责显示新的视图控制器。如下图所示:
注意:上面描述的definesPresentationContext功能只在iPad上起作用,并且视图控制器的modalPresentationStyle属性值需为UIModalPresentationCurrentContext,这样也就能实现模态形式显示的视图控制器的视图局部遮掩的效果了。
以上内容是本人结合开发经验并参考了部分资料而撰写的,如有不妥之处还请不吝赐教,愿大家能够互相学习,共同进步。