本教程以您在首个教程(“教程:基础”)中创建的项目为基础。您将会实践已学到的知识,包括视图、视图控制器、操作和导航方法。按照界面优先的设计流程,您还将为 ToDoList 应用程序创建某些关键的用户界面,并将行为添加到所创建的场景中。
本教程将向您讲述如何:
采用“Auto Layout”为用户界面增添灵活性
使用串联图来定义应用程序内容和流程
管理多个视图控制器
给用户界面中的元素添加操作
完成本教程中的所有步骤后,您的应用程序外观大致是这样的:
“add-to-do-item”场景的工作模式为竖排,与创建时一致。要是用户旋转了设备会如何?请试着在 Simulator 中运行应用程序来模拟这种情况。
在 iOS Simulator 中进行旋转
请在 iOS Simulator 中启动应用程序。
选取“Hardware”>“Rotate Left”(或按下 Command–左箭头键)。
如您所见,文本栏看起来不大对劲。它只占了屏幕约一半的位置。文本栏本应延伸至整个屏幕,正如竖排模式中显示的那样。幸好,Xcode 内建有一款功能强劲的布局引擎,称为“Auto Layout”。关于场景中要如何放置元素,您可以通过 Auto Layout 将您的意图描述出来,然后由该布局引擎确定如何以最优方案实现该意图。使用约束规则描述您的意图,它说明了应当如何放置一个元素以与另一个元素相关联、元素应有的大小,或者在适用的空间减小时,两个元素中的哪一个应当先缩小。对于“add-to-do-item”场景,则需要使用两类约束:一种用于放置文本栏,另一种用于设定其大小。
这些约束的设定工作均可在 Interface Builder 中轻松完成。
使用 Auto Layout 放置文本栏
在项目导航器中,选择Main.storyboard。
在串联图中,选择文本栏。
在画布上,按住 Control 键从文本栏拖向场景顶部,直到文本栏四周皆为空白区域。此区域为文本栏的父视图。
停止拖移操作时,其位置将出现一个快捷菜单。
从快捷菜单中选取“Top Space to Top Layout Guide”。
在文本栏顶部和导航栏之间,将创建一个间距约束。
如果出现了其他菜单(比如带有“Leading Space to Container”菜单项),则表示您没有垂直地拖到屏幕顶部。Xcode 会根据您拖移的方向来判断您打算生成哪一类约束,并根据拖移的起始点和结束点来判断约束要关联哪些对象。您可以继续尝试不同的拖移方向,以便了解有哪些约 束可用。
尝试完之后,请按住 Control 键从文本栏向右拖移到父视图,以便创建“Trailing Space to Container”约束。
按住 Control 键从文本栏向左拖移到其父视图,以便创建“Leading Space to Container”约束。
这些约束指定了文本栏边缘与其父视图之间的距离,使它们不发生变化。也就是说,如果设备方向发生改变,文本栏将自动伸展以满足这些约束。
检查点:运行您的应用程序。如果您旋转设备,文本栏将根据设备的方向伸展或收缩到适当大小。
如果行为没能达到预期,请启用 Xcode Auto Layout 调试功能以获取帮助。文本栏选定后,请选取“Editor”>“Resolve Auto Layout Issues”>“Reset to Suggested Constraints”,让 Xcode 设置上述步骤所描述的约束。或选取“Editor”>“Resolve Auto Layout Issues”>“Clear Constraints”,移除文本视图上的所有约束,然后试着再次按照上述步骤操作。
尽管添加项目场景能做的有限,但基本的用户界面都已成型并且初具功能。在一开始考虑清楚布局问题,将为您之后的构建奠定坚实基础。
目前,您已经处理过由视图控制器管理的单个场景,即一个可让您将项目添加到待办事项列表的页面。现在,是时候创建一个能显示整个待办事项列表的场景了。真是幸运,iOS 自带一个名为表格视图的内建类,它功能强大,专门设计用于显示项目的滚动列表。
将带有表格视图的场景添加到串联图
在项目导航器中,选择Main.storyboard。
在实用工具区域中打开对象库。(若要通过菜单命令打开资源库,请选取“View”>“Utilities”>“Show Object Library”。)
将“Table View Controller”对象从列表中拖出,并放置在“add-to-do-item”场景左侧的画布上。如有需要,您可以使用画布右下方的“缩小”按钮 来获取足够的拖放空间。
如果表格视图中包含内容,但尝试拖到画布时却毫无反应,那么您拖移的很可能是表格视图,而不是表格视图控制器。表格视图是表格视图控制器所管理的多个项目之一,但您需要的是整个套件,因此请找到表格视图控制器,并将其拖到画布。
现在您有了两个场景,一个用于显示待办事项列表,另一个用于添加待办事项。
当用户启动应用程序时,让他们先看到列表很有必要。因此请告诉 Xcode 您的意图,把表格视图控制器设置为首个场景。
将表格视图控制器设定为初始场景
在大纲视图中,请选择新添加的表格视图控制器。
在“Attributes”检查器中,选择“Is Initial View Controller”选项旁的复选框。
或者,您可以将初始场景指示器从“add-to-do-item”场景中直接拖到画布上的表格视图控制器。
表格视图控制器将设定为串联图中的初始视图控制器,从而成为应用程序启动时载入的首个场景。
检查点:运行您的应用程序。现在,您看到的不是“add-to-do-item”场景及其文本栏,而应该是空的表格视图:一个由多条水平分隔条划分成行,但每一行均不包含内容的屏幕。
由于您尚未学习如何储存数据,因此要创建和储存待办事项,并将它们显示在表格视图中还为时过早。但是要展示用户界面原型,并不需要用到真实数据。 Xcode 允许您通过 Interface Builder 在表格视图中创建静态内容。此功能便于您查看用户界面的表现,并且对于尝试不同的设计概念来说也很有价值。
在表格视图中创建静态单元格
在界面的大纲视图中,选择“Table View Controller”下方的“Table View”。
在“Attributes”检查器中,从“Content”选项旁的弹出式菜单中选取“Static Cells”。
表格视图中将出现三个空的表格视图单元格。
在大纲视图中或画布上,选择顶部单元格。
在“Attributes”检查器中,从“Style”选项旁的弹出式菜单中选取“Basic”。
“Basic”样式包括标签,因此 Xcode 将在表格单元格中创建一个带有“Title”文本的标签。
在大纲视图中或画布上,选择该标签。
在“Attributes”检查器中,将标签文本从“Title”更改为“Mow the Lawn”。请按下 Enter 键,或点按实用工具区域的外部,使更改生效。
或者,您可以通过连按两下标签,然后直接编辑文本来编辑标签。
对于其他单元格,请重复步骤 4–7,并根据其他类似的待办事项给单元格安排文本。
请创建足够的单元格,使项目数量超过屏幕的显示范围。您可以通过拷贝和粘贴来创建新单元格,或通过按住 Option 键拖移单元格来进行创建。
检查点:运行您的应用程序。现在,您应该能看到一个表格视图,其中含有在 Interface Builder 中添加的预先配置的单元格。可以查看一下新表格视图在滚动时的整体效果。试试旋转模拟设备,注意观察单元格视图如何配置,才对其内容进行了正确布局。在表 格视图中,您可以自由地进行大量操作。
完成后,应该设计如何从这个表格视图及其待办事项列表,浏览到您所创建的首个场景(即用户可以创建新待办事项的场景)。
您已经在串联图中配置了两个视图控制器,但它们彼此之间没有连接。场景之间的转场,我们称为过渡。
创建过渡前,您需要先配置场景。首先,在导航控制器中包括待办事项列表表格视图控制器。回想之前的“定义交互”,我们知道导航控制器能提供导航栏,并且能够跟踪导航堆栈。您将在“add-to-do-item”场景的转场中添加导航栏按钮。
将导航控制器添加到表格视图控制器
在大纲视图中,请选择“Table View Controller”。
视图控制器选定后,请选取“Editor”>“Embed In”>“Navigation Controller”。
Xcode 能给串联图添加新的导航控制器、为其设定初始场景,并在新的导航控制器和现有的表格视图控制器之间建立关系。在画布上,如果您选择了连接两个场景的图标, 就会发现这其实是一种根视图控制器关系。这表示导航栏下方显示的内容视图将为表格视图。初始场景设定为导航控制器,因为导航控制器保留了将显示在应用程序 中的所有内容,它是待办事项列表和“add-to-do-item”场景的容器。
检查点:运行您的应用程序。在表格视图上方,您将看到额外的区域。这是导航控制器所提供的导航栏。
现在,您将为导航栏添加标题(关于待办事项列表)和按钮(用于添加更多待办事项)。
配置导航栏
在大纲视图中或画布上,选择“Table View Controller”下方的“Navigation Item”。
导航栏本身并不带标题,它们的标题来自导航控制器当前显示的视图控制器。您需要使用待办事项列表的导航项目(表格视图控制器)来设定标题,而不是直接在导航栏上设定。
在“Attributes”检查器中,在“Title”栏键入“My To-Do List”。
如有需要,请打开对象库。
在表格视图控制器中,将“Bar Button Item”对象从列表拖到导航栏的最右端。
在您拖移栏按钮项目的位置,将出现一个包含“Item”文本的按钮。
在大纲视图或画布上,选择栏按钮项目。
在“Attributes”检查器中,找到“Bar Button Item”部分中的“Identifier”选项。从“Identifier”弹出式菜单中,选取“Add”。
该按钮将变成添加按钮 (+)。
检查点:运行您的应用程序。导航栏现在应该有了标题,并显示有一个添加按钮。该按钮暂时没有任何作用。之后您将修复这个问题。
您想让添加按钮调用“add-to-do-item”场景。该场景已经配置好了,它正是您所创建的首个场景,只是还没有连接其他场景。通过 Xcode,您能轻松地配置添加按钮,使其轻按一下便可调出其他场景。
配置添加按钮
在画布上,选择添加按钮。
按住 Control 键从按钮拖到“add-to-do-item”视图控制器。
拖移停止的位置,将出现一个标题为“Action Segue”的快捷菜单。
通过该菜单,Xcode 能让您选取当用户轻按添加按钮时,待办事项列表到“add-to-do-item”视图控制器之间的转场所使用的过渡类型。
从快捷菜单中选取“push”。
Xcode 将设置过渡,并将“add-to-do-item”视图控制器配置为显示在导航控制器中,您将在 Interface Builder 中看到导航栏。
现在,项目中可能出现了数个警告。请继续并打开“Issue”导航器来查看问题所在。
由于您将“add-to-do-item”场景添加到了导航堆栈,它现在将显示导航栏。该栏将导致您文本栏的边框下移,从而无法满足之前指定的 Auto Layout 约束。好在这并不难解决。
更新 Auto Layout 约束
在大纲视图或画布上,选择文本栏。
在画布上,打开“Resolve Auto Layout Issues”弹出式菜单 ,然后选取“Update Constraints”。
或者,您可以选取“Editor”>“Resolve Auto Layout Issues”>“Update Constraints”。
约束将更新,同时 Xcode 警告会消失。
检查点:运行您的应用程序。您可以点按添加按钮,并浏览到表格视图中的“add-to-do-item”视图控制器。由于您使用的是带 push 过渡的导航控制器,因此向后导航已为您处理完毕。这表示您可以通过点按返回按钮来回到表格视图。
一般情况下,push 导航的功能是正常的,但添加项目时则不太一样。Push 导航设计用于深层次界面,即无论用户选定哪一项,您都能为其提供更多相关信息。而另一方面,添加项目是一种模态操作,即用户执行某个完整且自包含的操作, 然后从场景返回到主导航。此类型场景恰当的表述方式为 modal 过渡。
更改过渡样式
在大纲视图中或画布上,选择表格视图控制器到“add-to-do-item”视图控制器的过渡。
在“Attributes”检查器中,从“Style”选项旁的弹出式菜单中选取“Modal”。
Modal 视图控制器不会添加到导航堆栈,因此它没有从表格视图控制器的导航控制器中获得导航栏。但是您可能想要保留导航栏,给予用户视觉上的连续性。要在模态展示时为“add-to-do-item”视图控制器添加导航栏,请将它嵌入其自身的导航控制器中。
将导航控制器添加到“add-to-do-item”视图控制器
在大纲视图中,请选择“View Controller”。
视图控制器选定后,请选取“Editor”>“Embed In”>“Navigation Controller”。
和之前一样,Xcode 会在视图控制器的顶部添加导航控制器,并显示导航栏。接着,配置此栏以将标题及“Cancel”和“Done”这两个按钮添加到该场景。稍后,会将这些按钮链接到操作。
配置“add-to-do-item”视图控制器中的导航栏
在大纲视图中或画布上,选择“View Controller”下方的“Navigation Item”。如有需要,请打开“Attributes”检查器 。
在“Attributes”检查器中,在“Title”栏键入“Add To-Do Item”。
Xcode 会将视图控制器的描述从“View Controller”更改为“View Controller – Add To-Do Item”,方便您辨别场景。描述将出现在大纲视图中。
在“add-to-do-item”视图控制器中,将“Bar Button Item”对象从对象库拖到导航栏的最右端。
在“Attributes”检查器中,从“Identifier”选项旁的弹出式菜单中选取“Done”。
按钮文本将更改为“Done”。
在“add-to-do-item”视图控制器中,将另一个“Bar Button Item”对象从对象库拖到导航栏的最左端。
在“Attributes”检查器中,从“Identifier”选项旁的弹出式菜单中选取“Cancel”。
按钮文本将更改为“Cancel”。
检查点:运行您的应用程序。点按添加按钮。您仍将看到添加项目场景,但返回浏览待办事项列表的按钮将消失,取而代之的是您添加的 “Done”和“Cancel”这两个按钮。这些按钮尚未链接到任何操作,因此您虽然可以点按它们,但不会有任何反应。下个任务,我们将配置按钮以完成或 取消编辑新的待办事项,并使用户返回待办事项列表。
至今您所完成的配置工作,都无需编写任何代码。不过,配置整个“add-to-do-item”视图控制器还是需要代码的,同时还得有放代码的位置。此 时,Xcode 已将“add-to-do-item”视图控制器,以及表格视图控制器,配置为基本的视图控制器。如果给自定代码预留位置,您需要为每一个视图控制器创建 子类,然后配置界面以使用这些子类。
首先,需要处理“add-to-do-item”视图控制器场景。自定视图控制器类的名称为XYZAddToDoItemViewController,因为此视图控制器所控制的场景,会为您的待办事项列表添加新项目。
创建 UIViewController 的子类
请选取“File”>“New”>“File”(或按下 Command-N)。
在出现的对话框左侧,选择“iOS”下方的“Cocoa Touch”模板。
选择“Objective-C Class”,然后点按“Next”。
在“Class”栏中,在XYZ前缀之后键入AddToDoItem。
选取“Subclass of”弹出式菜单中的UIViewController。
类标题将变更为“XYZAddToDoItemViewController”。创建自定视图控制器时,Xcode 能帮您清楚区分出该名称。好的,那这个新名称可以不用管了。
请确定“Targeted for iPad”和“With XIB for user interface”选项并未选定。
点按“Next”。
存储位置默认为您的项目目录。保持该项不变。
“Group”选项默认为您的应用程序名称,“ToDoList”。保持该项不变。
在“Targets”部分中,应用程序默认为选定,而应用程序的测试默认为未选定。很好,那这一项也保持不变。
点按“Create”。
现在,自定视图控制器的子类已经创建,您需要让串联图使用自定类,而不是使用基本视图控制器。串联图文件是应用程序运行时所使用的对象的相关配置。应用程 序的运行机理,能将串联图最初使用的基本视图控制器,智能地替换成自定视图控制器,但首先您需要向串联图下达该命令。
将类识别为某个场景的视图控制器
在项目导航器中,请选择Main.storyboard。
在大纲视图中,选择“View Controller – Add To-Do Item”视图控制器。
点按“View Controller – Add To-Do Item”场景旁的显示三角形来显示场景中的对象。第一个对象应该是视图控制器。点按以选择它。注意场景行的图标与视图控制器行的图标并不相同。
选定视图控制器后,请打开实用工具区域中的“Identity”检查器 。
当您点按左起第三个按钮时,“Identity”检查器将出现在实用工具区域的顶部。它能让您编辑串联图中对象的属性(与对象的识别信息相关),例如它是什么类。
在“Identity”检查器中,打开“Class”选项旁的弹出式菜单。
您将看到一个列表,其中包含了所有 Xcode 了解的视图控制器类。列表的最后一个应该是自定视图控制器XYZAddToDoItemViewController。选取它,从而让 Xcode 为此场景使用视图控制器。
运行时,串联图将创建XYZAddToDoItemViewController的实例,即您的自定视图控制器子类来代替基本的UIViewController。 请注意 Xcode 已将“add-to-do-item”视图控制器场景的描述,从“View Controller – Add To-Do Item”更改为“Add To Do Item View Controller – Add To-Do Item”。Xcode 知道此场景当前正在使用自定视图控制器,因此它解释了自定类的名称,以方便您理解串联图上的内容。
现在,请对表格视图控制器执行相同操作。
创建 UITableViewController 的子类
请选取“File”>“New”>“File”(或按下 Command-N)。
在左侧选择“iOS”下方的“Cocoa Touch”,然后选择“Objective-C Class”。如果在本教程前面的步骤中您尚未创建任何类,那么 Xcode 可能已经为您选好了一个。
点按“Next”。
在“Class”栏中,键入“ToDoList”。请注意 Xcode 将插入点放置在XYZ(类前缀)和ViewController(所创建的项目类型)之间。
从“Subclass of”弹出式菜单中选取UITableViewController。
请确定“Targeted for iPad”和“With XIB for user interface”选项并未选定。
点按“Next”。
存储位置默认为您的项目目录。保持该项不变。
“Group”选项默认为您的应用程序名称,“ToDoList”。保持该项不变。
在“Targets”部分中,应用程序默认为选定,而应用程序的测试默认为未选定。很好,那这一项也保持不变。
点按“Create”。
请再次确定,已在串联图中配置了自定表格视图控制器XYZToDoListViewController。
配置串联图
在项目导航器中,选择Main.storyboard。
如有需要,请打开大纲视图。
在大纲视图中,选择表格视图控制器,然后打开实用工具区域中的“Identity”检查器 。
在“Identity”检查器中,从“Class”选项旁的弹出式菜单中选取XYZToDoListViewController。
现在,您可以给视图控制器添加自定代码了。
除了 push 和 modal 过渡外,Xcode 还提供 unwind 过渡。此过渡允许用户从一个给定场景返回上一个场景,并提供位置让您添加代码,以在用户导航切换场景时执行。您可以使用 unwind 过渡从XYZAddToDoItemViewController返回浏览XYZToDoListViewController。
通过向目的视图控制器(要跳转到的视图控制器)添加操作方法,可以创建 unwind 过渡。可以跳转到的方法必须返回一个操作 (IBAction),并且在串联图过渡 (UIStoryboardSegue) 中作为参数采用。您可能想跳转回XYZToDoListViewController,因此需要将带有该格式的操作方法添加到XYZToDoListViewController界面并进行实现。
跳转回 XYZToDoListViewController
在项目检查器中,打开XYZToDoListViewController.h。
在@interface行下方添加以下代码:
在项目检查器中,打开XYZToDoListViewController.m。
在@implementation行下方添加以下代码:
您可以给跳转操作随意命名。可以将它命名为unwindToList:,让跳转操作返回的位置一目了然。对于今后的项目,可以采用类似的命名惯例,即操作的名称能清楚表示跳转操作将返回的位置。
当前项目中,请将此方法实现留空。稍后,您将使用此方法从XYZAddToDoItemViewController中取回数据,以将项目添加到待办事项列表。
若要创建 unwind 过渡,请通过源视图控制器XYZAddToDoItemViewController场景台中的“Exit”图标,将“Cancel”和“Done”按钮链接到unwindToList:操作。
将按钮链接到“unwindToList:”操作
在项目导航器中,选择Main.storyboard。
在画布上,按住 Control 键从“Cancel”按钮拖到“add-to-do-item”场景台中的“Exit”项。
如果您在场景台中看到的是场景的描述,而不是“Exit”项,请点按画布上的“放大” 按钮,直到看到该项为止。
一个菜单将出现在拖移停止的位置。
从快捷菜单中选取unwindToList:。
这是您刚才添加到XYZToDoListViewController.m文件的操作。表示当轻按“Cancel”按钮时,将跳转过渡并调用此方法。
在画布上,按住 Control 键从“Done”按钮拖到XYZAddToDoItemViewController场景台中的“Exit”项。
从快捷菜单中选取unwindToList:。
请注意,“Cancel”和“Done”按钮使用了相同的操作。在下个教程中,您将在编写处理 unwind 过渡的代码时区分这两种情况。
检查点:现在,请运行应用程序。启动时,您将看到表格视图,但其中不含数据。您可以点按添加按钮,从XYZToDoListViewController浏览到XYZAddToDoItemViewController。也可以点按“Cancel”和“Done”按钮返回浏览表格视图。
那为什么显示不了数据呢?表格视图有两种获取数据的方式,即静态获取或动态获取。当表格视图的控制器实现所要求的UITableViewDataSource方法时,表格视图将向其视图控制器请求数据以进行显示,不管 Interface Builder 中是否配置了静态数据。如果您留意下XYZToDoListViewController.m,就会发现它实现了三种方法:numberOfSectionsInTableView:,tableView:numberOfRowsInSection:以及tableView:cellForRowAtIndexPath:。您可以通过注释掉这些方法的实现,在表格视图中再次显示静态数据。如果感兴趣,您可以继续试试。
现在,您已经完成了应用程序界面的开发工作。现在有两个场景:一个用于给待办事项列表添加项目,另一个用于查看该列表;并且您可以在两个场景之间浏览。接下来要实现的功能,是让用户添加新的待办事项,并使其显示在列表中。下一部分讲述了如何处理数据以实现该行为。