视图控制器
概览
视图控制器的作用
视图控制器是应用内部结构的基础。
UIViewController类定义了用于管理视图,处理事件,从一个视图控制器转换到另一个视图控制器以及与应用程序的其他部分协调的方法和属性。你可以继承UIViewController(或其某个子类)并添加实现应用程序行为所需的自定义代码。
两种类型的视图控制器:
- 内容视图控制器:管理应用内容的一个独立部分,是你创建的视图控制器的主要类型。
- 容器视图控制器:从子视图控制器收集信息,以便于以导航或其他方式呈现这些视图控制器的内容。
大多数应用程序是两种视图控制器的混合体。
视图管理
视图控制器最重要的作用是管理视图层次结构。每个视图控制器都有一个包含所有视图控制器内容的根视图。视图控制器始终具有对其根视图的引用,并且每个视图都具有对其子视图的强引用。
视图控制器和它的视图之间的关系:
注意:通常的做法是使用 outlets 访问视图控制器视图层次结构中的其他视图。因为视图控制器管理其所有视图的内容,outlets 让你存储需要视图的引用。当视图从 storyboard 加载时,outlets 会自动连接到实际的视图对象。
内容视图控制器自己管理其所有视图。容器视图控制器管理自己的视图以及其子视图控制器的根视图。容器视图控制器不管理子视图控制器的内容。它只管理根视图,根据容器设计调整并放置它。
分割视图控制器管理其子视图的整体大小和位置,但子视图控制器管理这些视图的实际内容。
数据封装
视图控制器充当其管理的视图和应用程序数据之间的中介。当你继承 UIViewController 时,你可以添加任何需要在你的子类中管理数据的变量。视图控制器引用了你的数据和用于呈现该数据的视图。在两者之间来回移动数据是你的责任。你应始终在视图控制器和数据对象内保持清晰的职责分离。
用户交互
视图控制器是响应者对象,能够处理响应者链中发生的事件。虽然他们能够这样做,但视图控制器很少直接处理触摸事件。相反,视图通常会处理自己的触摸事件,并将结果报告给关联的代理或目标对象的方法,该对象通常是视图控制器。因此,视图控制器中的大多数事件都是使用代理(delegate)方法或操作(action)方法处理的。
资源管理
视图控制器承担其视图及其创建的任何对象的所有责任。UIViewController 类自动处理视图管理的大多数方面。例如,UIKit 会自动释放不再需要的任何视图相关资源。在你的 UIViewController 子类中,你负责管理你明确创建的任何对象。
当可用空闲内存不足时,UIKit 会要求应用程序释放不再需要的资源。其中一种方法是调用视图控制器的 didReceiveMemoryWarning 方法。当您呈现视图控制器时,UIKit会查找为呈现提供合适上下文的视图控制器。
适应性
视图控制器层次结构
根视图控制器
根视图控制器是视图控制器层次结构的锚点。每个窗口都有一个根视图控制器,其内容填充该窗口。根视图控制器定义用户看到的初始内容。由于窗口本身没有可见的内容,所以视图控制器的视图提供了所有的内容。
根视图控制器和窗口之间的关系:根视图控制器可以从 UIWindow 对象的 rootViewController 属性访问。当你使用 storyboard 来配置视图控制器时,UIKit 会在启动时自动设置该属性的值。对于以编程方式创建的窗口,您必须自己设置根视图控制器。
容器视图控制器
容器视图控制器允许你从更易于管理和可重用的部分组装复杂的界面。容器视图控制器将一个或多个子视图控制器的内容与可选的自定义视图混合在一起以创建其最终界面。例如, UINavigationController 对象显示来自子视图控制器的内容以及由导航控制器管理的导航栏和可选工具栏。UIKit包含多个容器视图控制器,包括 UINavigationController,UISplitViewController 和 UIPageViewController。
容器视图控制器的视图始终填充给定的空间。容器视图控制器通常作为根视图控制器安装在窗口中。但它们也可以模态呈现或安装为其他容器的子项。容器负责适当地定位其子视图。虽然它取决于容器接口,但子视图控制器可能对容器和任何同级视图控制器有最少的了解。由于容器视图控制器管理其子项,UIKit 定义了如何在自定义容器中设置这些子项的规则。
被呈现的视图控制器
呈现视图控制器会将当前视图控制器的内容替换为新视图控制器的内容,通常会隐藏前一个视图控制器的内容。呈现(Presentation)通常用于以模态方式显示新内容。例如,您可能会提供一个视图控制器来收集用户的输入。您也可以将它们用作应用程序界面的一般构建块。
呈现视图控制器时,UIKit 会在呈现视图控制器和被呈现的视图控制器之间创建关系。从被呈现的视图控制器返回到其呈现的视图控制器也存在相反的关系。这些关系构成视图控制器层次结构的一部分,并且是在运行时查找其他视图控制器的一种方法。
被呈现视图控制器:不同的呈现(Presentation)风格对于它们在屏幕上的显示方式有不同的规则 - 例如,全屏呈现(full-screen presentation)总是覆盖整个屏幕。在许多情况下,UIKit 选择最近的容器视图控制器,但它也可能选择窗口的根视图控制器。在某些情况下,您还可以告诉 UIKit 哪个视图控制器定义了呈现上下文(context)并应处理呈现。
容器通常为呈现提供上下文(context)。在执行全屏呈现时,新视图控制器需要覆盖整个屏幕。容器决定是否处理呈现,而不是要求子视图控制器知道容器的边界。由于示例中的导航控制器覆盖了整个屏幕,因此它充当呈现视图控制器并启动呈现。
容器和被呈现视图控制器:
设计技巧
尽可能使用系统提供的视图控制器
在创建自己的自定义视图控制器之前,请查看现有框架以确认你要执行的任务是否已存在视图控制器:
UIKit 框架提供视图控制器,用于显示警报,拍照和录像以及管理iCloud上的文件。UIKit 还定义了许多可用于组织内容的标准容器视图控制器。
GameKit 框架提供了用于匹配玩家的视图控制器以及管理排行榜,成就和其他游戏功能。
Address UI 框架提供用于显示和选择联系人信息的视图控制器。
MediaPlayer 框架提供用于播放和管理视频,以及从用户库中选择媒体资产的视图控制器。
EventKit UI 框架提供了用于显示和编辑用户日历数据的视图控制器。
GLKit 框架提供了一个用于管理 OpenGL 渲染表面的视图控制器。
Multipeer Connectivity 框架提供了用于检测其他用户并邀请他们进行连接的视图控制器。
Message UI 框架提供了用于编写电子邮件和 SMS 消息的视图控制器。
PassKit 框架提供视图控制器来显示通行证并将其添加到存折。
Social 框架为 Twitter,Facebook 和其他社交媒体网站提供了编写消息的视图控制器。
AVFoundation 框架提供了用于显示媒体资产的视图控制器。
使每个视图控制器成为一个孤岛
视图控制器应始终是自包含的对象。没有视图控制器应该具有有关另一个视图控制器的内部工作或视图层次结构的信息。在两个视图控制器需要来回通信或传递数据的情况下,它们应该始终使用明确定义的公共接口来实现。
代理设计模式经常用于管理视图控制器之间的通信。通过代理,一个对象定义了一个协议,用于与关联的代理对象进行通信,该代理对象可以是遵循协议的任何对象。代理对象的确切类型不重要。重要的是它实现了协议的方法。
仅将根视图用作其他视图的容器
仅将视图控制器的根视图用作其余内容的容器。使用根视图作为容器为所有视图提供了一个共同的父视图,这使得许多布局操作变得更简单。许多自动布局约束需要共同的父视图来正确布置视图。
知道你的数据保存在哪里
在模型 - 视图 - 控制器(MVC)设计模式中,视图控制器的作用是促进模型对象和视图对象之间的数据流动。视图控制器可能会将一些数据存储在临时变量中并执行一些验证,但其主要职责是确保其视图包含准确的信息。您的数据对象负责管理实际数据并确保数据的完整性。