当你使用UIViewController的自定义子类来展示你应用内容。大多数自定义视图控制器是内容视图控制器,他们有自己的视图并负责管理这些视图中的数据。相比之下,容器视图控制器并不拥有所有的视图,一些视图是由其他视图控制器管理。大多数定义内容和容器视图控制器的步骤是一样的,这些将在下面几节中讨论。
内容视图控制器,最常见的父类如下:
·使用UITableViewController,尤其是当你的视图控制器的主要视图是表。
·使用UICollectionViewController,尤其是当你的视图控制器的主要视图是一个集合视图。
·其他所有的视图控制器使用UIViewController。
对于容器视图控制器,父类取决于是否修改一个已经存在的容器类或者创建你自己的。对于现有容器,选择你想要修改的视图控制器类。对于新的容器视图控制器,通常继承UIViewController。关于创建容器视图控制器的更多信息,参见实现容器视图控制器(Implementing a Container View Controller)。
定义UI
为视图控制器定义UI通常使用Xcode中的storyboard。虽然也可以通过编程创建UI,但storyboard是一个非常好的方法来可视化视图控制器的内容并在不同的环境(如果需要)定制你的视图层级。构建视觉UI可以让你快速的改变并让你看到结果,而无需构建和运行应用。
图4-1展示了storyboard的例子。每个矩形区域代表一个视图控制器和其相关的视图。视图控制器之间的箭头是视图间关系和联线。Relationships连接一个容器视图控制到它的子视图控制器。Segues让你在界面视图控制器之间导航。
每个新项目都有一个主storyboard,通常包含一个或多个视图控制器。通过拖放它们到画布上,可以添加新的视图控制器到storyboard。新视图控制器最初没有相关的类,你必须使用标示符赋值。
使用storyboard编辑器来完成以下操作:
·为一个视图控制器添加、整理并配置视图。
·连接outlet和action,参见处理用户交互(Handling User Interactions)。
·创建视图控制器之间的关系和联线,参见使用segues(Using Segues)。
·为不同的size类定义布局和视图,参见构建自适应界面(Building an Adaptive Interface)。
·添加手势识别器来处理用户交互视图,参见iOS事件处理指南(Event Handling Guide for iOS)。如果你是使用storyboard来建立你的界面,你可以在iOS应用开发中找到循序渐进的指示来创建基于storyboard的界面。
处理用户交互
应用的响应者对象处理传入的事件并采取适当的动作。虽然视图控制器是响应者对象,但他们很少直接处理触摸事件。相反,视图控制器通常以以下方式处理事件。
·视图控制器为处理高级别事件定义动作方法。动作方法响应:
1.指定动作。控制器和一些视图调用一个动作方法来响应特定交互。
2.手势识别器。手势识别器调用一个动作方法来响应当前手势状态。使用视图控制器来处理状态变化或响应完成的手势。
·视图控制器观察系统或其他对象发送的通知。通知报告变化,同时也是视图控制器更新状态的一种方式。
·视图控制器作为其他对象的数据源或代理。视图控制器通常用于管理表的数据、集合视图的数据。你可以使用它们作为对象的代理,例如CLLocationManager对象,发送更新的位置值到它的代理。
响应事件通常包括更新视图的内容,这需要有这些视图的引用。视图控制器可以定义任何视图的outlet。使用列表4-1中的语法来声明你的outlet属性。列表中的自定义类定义了两个outlet(IBOutlet指定的关键字)和一个动作方法(IBOutlet指定的返回类型)。虽然动作方法响应按钮点击事件,Outlet存储了storyboard中按钮和文本框中的引用。
列表4-1在视图控制器类中定义outlet和动作。
OBJECTIVE-C
<pre><code>
@interface
MyViewController:UIViewController
@property(weak,nonatomic)IBOutletUIButton*myButton;
@property(weak,nonatomic)IBOutletUITextField*myTextField;
-(IBAction)myButtonAction:(id)sender;
@end
</pre></code>
SWIFT
<pre><code>
classMyViewController:UIViewController{
@IBOutletweakvarmyButton:UIButton!
@IBOutletweakvarmyTextField:UITextField!
@IBActionfuncmyButtonAction(sender:id)
}
</pre></code>
在storyboard中,记得连接视图控制器的outlet和动作到相应的视图。连接storyboard中的outlet和动作确保党视图加载时他们已配置好。更多关于如何在IB中创建outlet和动作连接的方法,参见IB连接帮助(Interface Builder Connections Help)。更多关于如何处理应用中事件,参见iOS事件处理指南(Event Handling Guide for iOS)。
在运行时显示视图
Storyboard使加载和显示视图控制器的视图变得简单。UIKit自动从storyboard加载视图。随着加载过程,UIKit执行以下的任务序列:
1.使用storyboard文件中的信息实例化视图。
2.连接所有outlet和动作。
3.指定根视图为视图控制器的视图属性。
4.调用视图控制器的awakeFromNib方法。
当调用此方法,视图控制器的特征集合为空,视图可能不在最后的位置。
5.调用视图控制器的viewDidLoad方法。
使用该方法添加或删除视图,修改布局约束,加载视图数据。
在屏幕上显示视图控制器的视图前,UIKit提供额外的机会准备这些视图,之后显示在屏幕上。具体说来,UIKit执行以下任务序列:
1.调用视图控制器的viewWillAppear:方法让它知道视图将出现在屏幕上。
2.更新视图布局。
3.在屏幕上显示视图。
4.当视图在屏幕上调用viewDidAppear:方法。
当添加、删除或修改视图的大小或位置,记得添加和删除用于这些视图的约束。修改视图层级的布局相关变更会导致UIKit布局混乱。在接下来的更新周期,布局引擎计算视图的大小和位置,使用当前布局约束并将这些更改应用到视图层级结构。
关于如何创建视图而不使用storyboard的更多信息,参见UIViewController类引用(UIViewController Class Reference)中的视图管理信息。
管理视图布局
当视图的大小和位置发生变化,UIKit更新视图层级的布局信息。对于使用自动布局的视图,UIKit使用自动布局引擎并根据当前约束使用它更新布局。UIKit还允许其他对象,例如活动展示控制器,知道布局变化,这样它们可以做相应的回应。
在布局过程中,在几个点上UIKit发出通知,这样可以执行额外的布局相关任务。使用这些通知来修改布局约束或者在布局约束应用后做最后的布局调整。在布局过程中,UIKit为每个受影响的视图控制器做如下任务:
1.根据需要,更新视图控制器和视图的特征集合,参见特征和大小变更的时间(When Do Trait and Size Changes Happen)
2.调用视图控制器的viewWillLayoutSubviews方法。
3.调用当前UIPresentationController对象的containerViewWillLayoutSubviews方法。
4.调用视图控制器的根视图的layoutSubviews方法。
该方法默认使用可用约束实现计算新布局信息。该方法遍历视图层级并为每个子视图调用layoutSubviews。
5.运用计算好的布局信息到视图。
6.调用视图控制器的viewDidLayoutSubviews方法。
7.调用当前UIPresentationController对象的containerViewDidLayoutSubviews方法。
视图控制器可以使用viewWillLayoutSubviews和viewDidLayoutSubviews方法执行额外更新,这些更新可能会影响布局过程。布局之前,你可以添加或删除视图,更新视图的大小和位置,更新约束,或者更新其他视图相关属性。布局之后,可能重新加载表数据,更新其他视图的内容,或者最后调整视图大小和位置。
这里有些小提示可以有效的管理布局:
·使用自动布局。使用自动布局创建的约束是一种灵活和简单的方式在不同屏幕尺寸上定位你的内容。
·利用顶部和底部布局参考线。内容布局中的参考线确保内容总是可见的。顶部位置的布局参考线把状态栏和导航栏的高度考虑在内。同样的,底部位置的布局参考线把tab栏或工具栏的高度考虑在内。
·当添加或删除视图时记得更新约束。如果动态添加或删除视图,记得更新相应的约束。
·当视图控制器的视图在动画时,暂时删除约束。当使用UIKit核心动画完成视图动画,在动画期间删除约束并在动画完成时添加约束。如果在动画期间视图的位置或尺寸改变,记得更新约束。
关于显示控制器和视图控制器结构中的作用信息,参见present和transit过程(The Presentation and Transition Process)。
有效管理内存
尽管大多数方面的内存分配由你来决定,表4-1列出了的UIViewControll方法,这些方法最有可能出现分配或释放内存。大多数回收涉及删除对象的强引用。删除对象的强引用,设置指向改对象的属性和变量为nil。
任务 | 方法 | 讨论 |
---|---|---|
分配视图控制器所需的关键数据结构 | Initialization methods | 自定义initialization方法(无论是否名为init或其他)总是负责把视图控制器对象放到一个已知状态。使用这些方法来分配任何数据结构以确保正确的操作。 |
分配或加载视图中显示的数据 | viewDidLoad | 使用viewDidLoad方法加载要显示的任何数据对象。调用此方法时,保证视图对象存在并且处于已知状态。 |
响应低内存通知 | didReceiveMemoryWarning | 使用该方法释放视图控制器所有相关对象。尽量释放内存。 |
释放视图控制器的关键数据结构 | dealloc | 覆盖该方法在最后清理视图控制器类。系统自动释放实例变量对象及类属性,所以你不需要显示的释放它们。 |
表4-1进行分配和释放内存的地方