在iOS中,动态界面是通过视图控制器来管理的,视图控制器是UIViewController的实例。

一个视图控制器管理一个单独的视图,当然,这个视图还有子视图。视图控制器的view属性指向它管理的视图,即视图控制器的主视图(main view)。视图控制器的主视图并不拥有一个直接指向管理它的视图控制器的指针,但是视图控制器是UIResponder,在响应者链中刚好处在它的视图之上,所以视图控制器就是视图的nextResponder,即self.view.nextResponder() 其实就是self。

视图控制器的职责:

视图控制器最重要的责任是它的视图,一个视图控制器必须要有一个视图。如果那个视图想要有用的话,必须要以某种方式进入界面中,并且在某个时候位于屏幕之上,视图控制器负责这种实现,注意:这里所说的视图控制器并非管理上面提及的视图的视图控制器,而是管理的主视图已经在界面上得某个视图控制器。在许多情况下,这会自动发生,但是你可以参与这个过程,对于一些控制器来说,你必须要亲自做这个工作。

视图控制器在视图进入或者离开界面时,通过会提供动画。内置的视图控制器子类和内置的激活或者移除视图控制器与它的视图的方式通常有内置的动画。

最强大的视图控制器是根视图控制器(root view controller)。这是管理根视图(root view)的视图控制器,而根视图位于视图层次(view hierarchy)的顶端,作为主窗口的唯一直接视图和其它所有界面的父视图。根视图控制器负责两件非常重要的事情:

  • 界面的旋转
    用户可以旋转设备,你可以想要界面跟着旋转。这通常是由跟视图控制器来决定的。
  • 管理状态条(status bar)
    状态条通常属于运行时的第二个窗口,但运行时会询问根视图控制器状态条是否要展示,如果展示的话,它的文本是亮(light)还是暗(dark)

视图控制器层次(View Controller Hierarchy)

在一个应用中,总会有一个根视图控制器(root view controller),和它的视图,即根视图(root view)。可能也会有其它的视图控制器,每个视图控制器都有属于自己的主视图(main view),这些视图控制器是从属于根视图控制器的。在iOS中,视图控制器之间有两种从属关系

  • Parentage(Containment包含)

一个视图控制器可以包含另一个视图控制器,包含的视图控制器(containing view controller)是被包含的视图控制器(contained view controller)的父视图控制器;相反,被包含的视图控制器(contained view controller)是包含的视图控制器(containing view controller)的子视图控制器。两个视图控制器之间的包含关系同时也反映在它们的视图之中:子视图控制器的视图,是父视图控制器的视图的子视图。

父视图控制器负责将子视图控制器的视图放到界面上,通过让子视图控制器的视图成为它的视图的子视图。一个视图的引入、移除和取代,往往会涉及到父视图控制器管理它的子视图控制器以及它们的视图。

  • Presentation(modal view模态视图)

一个视图控制器可以展示另一个视图控制器,第一个视图控制器是第二个视图控制器的展示的视图控制器(presenting view controller),并非父视图控制器。而第二个视图控制器是第一个视图控制器的被展示的视图控制器(presented view controller)。第二个视图控制器的视图会部分或者完全取代或覆盖第一个视图控制器的视图。

视图控制器之间的这种关系的名称也在改变。在iOS4以及之前,被展示的视图控制器叫做模态视图控制器(modal view controller),它的视图是模态视图(modal view)。术语”被展示的视图控制器(presented view controller)”和”展示的视图控制器(presenting view controller)”。但是历史的术语”模态(modal)”仍然出现在文档和API中。

从上面的介绍来看,因此有一个视图控制器的层次。在一个合理架构的iOS应用中,应当只有一个根视图控制器,它是一个唯一没有从属的视图控制器:它既没有父视图控制器,也没有展示的视图控制器(presenting view controller)。任何其它的视图控制器,如果它的视图控制器出现在界面上的话,必须是某个父视图控制器的子视图控制器,或者是某个展示的视图控制器的被展示视图控制器。

在视图层次(view hierarchy)和视图控制器层次(view controller hierarchy)。对于一个父视图控制器和一个子视图控制器,如果子视图控制器的视图要展示在界面上的话,必须要是父视图控制器的视图的子视图。相似地,对于一个展示的视图控制器和被展示的视图控制器,被展示的视图控制器的视图要么是展示的视图控制器的视图的子视图,要么完全取代展示展示的视图控制器的视图。

视图控制器的视图在视图层次中的位置通常是自动的。你可能永远都不需要手动将UIViewController的视图放进视图层次中。你只需要管理视图控制器,它们的层次和内置的功能会为你构建和管理视图层次。

下面看一下,导航控制器是如何通过控制器层次和视图层次来显示应用界面的。

ios 子视图获取控制器 ios视图控制器的功能_ios 子视图获取控制器

解释一下:

应用的根视图控制器是UINavigationController, 它的视图是窗口的直接子视图(根视图),导航条是那个视图的子视图。

UINavigationController包含第二个UIViewController:父-子关系。子视图控制器是一个UIviewController的子类,它的视图占据窗口的剩下部分,作为UINavigationController视图的另一个子视图。这种架构意味着用户点击切换的时候,整个第二个视图控制器的视图会滑动离开,被不同的视图控制器的视图取代,而导航条仍然存在。

有时候,你需要编写自己的父视图控制器类。这种情况下,你必须要手动将子视图控制器的视图放到界面中,作为父视图控制器的视图的子视图。

注意:

不要手动将一个视图控制器的视图放到界面中,除非是下面这种情况:

  • 除非该视图控制器是你自定义的父视图控制器的子视图控制器。
  • 你要做一个自定义的转场动画

视图控制器如何获取它的视图?

视图控制器是一个小的、轻量级的对象;而视图是有一个相对重量级的对象,包含了占据内容的界面元素。视图控制器对它的视图是进行懒加载的。

对于刚实例化的视图控制器,注意:如果我们不需要的话,尽量不要获取它的视图,这样会导致视图控制器过早地获取它的视图。为了知道一个视图控制器是否已经有了一个视图,但是却不想要视图控制器加载视图,我们可以调用视图控制器的方法public func isViewLoaded() -> Bool,在iOS9中,你可以安全地获取视图控制器的视图,而不用加载视图,通过视图控制器的只读属性:public var viewIfLoaded: UIView? { get },如果视图已经加载的话,返回控制器的视图,如果没加载的话,返回nil。

只要视图控制器拥有了它的视图,它的ViewDidLoad方法就已经被调用了。

被展示的视图控制器(Presented View Controller)

回顾只有iPhone(没有iPad)的年代,被展示的视图控制器叫做模态视图控制器。当模态视图控制器展示的时候,根视图控制器停留在原来的地方,但是它的视图被拿出了界面,模态视图控制器的视图被使用。因此,这是最简单的方式来用一个不同的界面来取代整个界面。

但是,模态这个特点不总是胜任的。因为被展示的视图控制器的视图可能会有一个复杂的界面,它可能有子视图控制器等。而且,一个被展示的视图控制器的视图显示出来的方式不仅仅是取代根视图控制器的视图那样,例如:

  • 一个被展示的视图控制器的视图可以取代界面上的一个子视图,而不是取代整个界面(这种特点之前只有iPad拥有,iOS8后,iPhone也有)
  • 一个被展示的视图控制器的视图值覆盖界面的部分,存在的界面永远不会移除(这之前也是iPad的特点,iOS7以后iPhone也有了)

展示(Present)一个视图:

展示(present)和dismiss一个视图的两个关键方法:

  • public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)
  • public func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?)

注意:展示的(presenting)视图控制器(被展示的(presented)视图控制器的presentingViewController)不一定是发送presentViewController:animated:completion:消息的控制器。好好理解下面三个概念:

  • Presented view controller被展示的视图控制器
    这个视图控制器作为presentViewController:animated:completion的第一个参数
  • Original presenter初始展示者
    这个视图控制器指的是发送presentViewController:animated:completion消息的视图控制器。苹果有时候将这个视图控制器称为source,Original presenter是我使用的术语
  • Presenting view controller展示的视图控制器
    被展示的视图控制器的presentingViewController。被展示的视图控制器的视图会将这个视图控制器的视图取代或者覆盖。

被展示的视图的动画:

一个视图被展示或者dismiss时候,可以执行一些动画,有一些不同内置的动画样式(modal transition模态转场样式),当然,你也可以提供自定义的动画。

记住,内置的动画样式是通过将其作为被展示的视图控制器的modalTransitionStyle属性设置。取值如下:

  • CoverVertical
  • FlipHorizontal
  • CrossDissolve
  • PartialCurl

Presentation Styles 展示样式:

默认,被展示的视图控制器的视图会占满整个屏幕,完全取代展示的视图控制器的视图。但是你可以选择另外的内置参数来改变被展示的视图控制器的视图覆盖屏幕的方式(modal presentation 模态展示样式)。通过设置被展示的视图控制器的modalPresentationStyle属性来设置。