本教程以第二个教程(“教程:串联图”)中创建的项目为基础。您将用到从使用设计模式、使用 Foundation 以及编写自定类中学到的知识,在 ToDoList 应用程序中添加对动态数据的支持。
本教程讲述了以下操作:
使用常见的 Foundation 类
创建自定数据类
实现委托和数据源协议
在视图控制器之间传递数据
完成本教程中的所有步骤后,您的应用程序外观大致是这样的:
现在就开始吧,请在 Xcode 中打开您的现有项目。
目前,使用串联图的 ToDoList 应用程序有一个界面和一个导航方案。现在,是时候使用模型对象来添加数据储存和行为了。
应用程序的目标在于创建一个待办事项列表,因此首先您将创建一个自定类XYZToDoItem来表示单个待办事项。您应该记得,XYZToDoItem类已经在编写自定类中讨论过。
创建 XYZToDoItem 类
选取“File”>“New”>“File”(或按下 Command-N)。
这时将会出现一个对话框,提示您为新文件选取模板。
从左侧的 iOS 下方选择“Cocoa Touch”。
选择“Objective-C Class”,并点按“Next”。
在“Class”栏中,在XYZ前缀后键入ToDoItem。
从“Subclass of”弹出式菜单中选取“NSObject”。
如果您完全按照本教程操作,那么在这个步骤之前,“Class”标题可能是XYZToDoItemViewController。选取 NSObject 作为“Subclass of”后,Xcode 会知道您创建了一个正常的自定类,并移除了它先前添加的ViewController文本。
点按“Next”。
存储位置默认为您的项目目录。此处无需更改。
“Group”选项默认为您的应用程序名称“ToDoList”。此处无需更改。
“Targets”部分默认选定您的应用程序,未选定应用程序的测试。好极了,这些都无需更改。
点按“Create”。
XYZToDoItem类很容易实现。它具有项目名称、创建日期,以及该项目是否已完成等属性。继续将这些属性添加到XYZToDoItem类接口。
配置 XYZToDoItem 类
在项目导航器中,选择XYZToDoItem.h。
将以下属性添加到该接口,使声明如下所示:
检查点:通过选取“Product”>“Build”(或按下 Command-B)来生成项目。尽管该新类尚未实现任何功能,但是生成它有助于编译器验证任何拼写错误。如果发现错误,请及时修正:通读编辑器提供的警 告或错误,然后回顾本教程中的说明,确保所有内容与此处的描述相符。
您现在有一个类,可以用它作为基础来为单个列表项目创建并储存数据。您还需要保留一个项目列表。在XYZToDoListViewController类中跟踪此内容较为合适,视图控制器负责协调模型和视图,所以需要对模型进行引用。
Foundation 框架有一个NSMutableArray类,很适合跟踪项目列表。此处必须使用可变数组,这样用户就可以将项目添加到数组。因为不可变数组NSArray在其初始化后将不允许添加项目。
要使用数组,您需要声明并创建它。可以通过分配并初始化数组来完成。
要分配并初始化数组
在项目导航器中,选择XYZToDoListViewController.m。
由于项目数组是表格视图控制器的实现细节,所以应该在 .m 文件中进行声明,而不是 .h 文件。此操作可让项目数组成为您自定类的私有数组。
将以下属性添加到接口类别中,它是由 Xcode 在您的自定表格视图控制器类中创建的。声明应该是这样的:
在viewDidLoad方法中分配并初始化toDoItems数组:
viewDidLoad的实际代码中有一些附加行被注释掉了,那些行是 Xcode 创建XYZListViewController时插入的。保留与否都没有影响。
现在,您已经拥有了一个可以添加项目的数组。添加项目将在单独的方法loadInitialData中进行,并将通过viewDidLoad调用该方法。由于此代码是一个模块化任务,所以会进入其自身的方法中。当然您也可以将方法分离出来,从而提高代码的可读性。在真正的应用程序中,此方法可 能会从某种永久储存形式载入数据,例如文件。现在,我们的目标是了解表格视图如何处理自定数据项目,那么让我们创建一些测试数据来体验一下吧。
以创建数组的方式创建项目:分配并初始化。然后,给项目命名。该名称将显示在表格视图中。按照此方法创建一组项目。
载入初始数据
在@implementation行下方,添加一个新方法loadInitialData。
在此方法中,创建几个列表项目,并将它们添加到数组。
在viewDidLoad方法中调用loadInitialData。
检查点:通过选取“Product”>“Build”来生成项目。您应该会在loadInitialData方法的代码行上看到大量错误。第一行是出错的关键所在,错误提示应该是“Use of undeclared identifier XYZToDoItem”。这说明编译器在编译XYZToDoListViewController时不知道XYZToDoItem。编译器比较特别,您需要明确告知它应当注意什么。
让编译器注意您的自定列表项目类
在XYZToDoListViewController.m文件的顶部附近找到#import "XYZToDoListViewController.h"行。
紧接着在其下方添加以下行:
检查点:通过选取“Product”>“Build”来生成项目。项目生成时应该没有错误。
目前,表格视图具有一个可变数组,预填充了几个示例待办事项。现在您需要在表格视图中显示数据。
通过让XYZToDoListViewController成为表格视图的数据源,可以实现这一点。无论要让什么成为表格视图的数据源,都需要实施UITableViewDataSource协议。需要实施的方法正是您在第二个教程中注释掉的那些。创建有效的表格视图需要三个方法。第一个方法是numberOfSectionsInTableView:,它告诉表格视图要显示几个部分。对于此应用程序,表格视图只需要显示一个部分,所以实现比较简单。
在表格中显示一个部分
在项目导航器中,选择XYZToDoListViewController.m。
如果您在第二个教程中注释掉了表格视图数据源方法,现在请移除那些注释标记。
模板实现的部分如下所示。
您想要单个部分,所以需要移除警告行并将返回值由 0 更改为 1。
更改numberOfSectionsInTableView:数据源方法以便返回单个部分,像这样:
下一个方法tableView:numberOfRowsInSection:告诉表格视图要在给定部分中显示几行。现在表格中有一个部分,并且每个待办事项在表格视图中都应该有它自己的行。这意味着行数应该等于toDoItems数组中的XYZToDoItem对象数。
返回表格中的行数
在项目导航器中,选择XYZToDoListViewController.m。
您可看到模板实现的部分是这样的:
您想要返回所拥有的列表项目的数量。幸运的是,NSArray有一个很方便的方法,称为count,它会返回数组中的项目数,因此行数是[self.toDoItems count]。
更改tableView:numberOfRowsInSection:数据源方法,使其返回正确的行数。
最后一个方法,tableView:cellForRowAtIndexPath:请求一个单元格来显示给定行。到现在为止,您只是处理了代码,但是界面的绝大部分是针对行显示的单元格。幸运的是,Xcode 可让您轻松地在 Interface Builder 中设计自定单元格。首个任务是设计您的单元格,并告诉表格视图不要使用静态内容,而要使用具有动态内容的原型单元格。
配置表格视图
打开串联图。
在大纲中选择表格视图。
在“Attributes”检查器中,将表格视图的“Content”属性从“Static Cells”更改为“Dynamic Prototypes”。
Interface Builder 会采用您配置的静态单元格,并将它们全部转换为原型。原型单元格,顾名思义,是使用您要显示的文本样式、颜色、图像或其他属性进行配置,并在运行时从数据 源获取其数据的单元格。数据源会为每一行载入一个原型单元格,然后配置该单元格来显示该行的数据。
要载入正确的单元格,数据源需要知道单元格的名称,并且该名称也必须在串联图中进行配置。
设定原型单元格名称时,也将配置另一个属性—单元格选择样式,该样式用于确定用户轻按单元格时单元格的外观。将单元格选择样式设定为“None”,使用户 轻按单元格时单元格不会高亮显示。这是当用户轻按待办事项列表中的项目,将其标记为已完成或未完成时,您想要单元格呈现的行为。稍后会在本教程中实现该功 能。
配置原型单元格
在表格中选择第一个表格视图单元格。
在“Attributes”检查器中,找到“Identifier”栏并键入ListPrototypeCell。
在“Attributes”检查器中,找到“Selection”栏并选取“None”。
您也可以更改原型单元格的字体或其他属性。基本配置很容易完成,您可以轻松记下。
下一步是实现tableView:cellForRowAtIndexPath:方法,让数据源为给定行配置单元格。表格视图在想要显示给定行时会调用此数据源方法。对于行数较少的表格视图,所有行可能会同时出现在屏幕上,所以表格中 的每一行都会调用此方法。但是,行数很多的表格视图在给定时间内只会显示全部项目中的一小部分。最有效的方式是让表格视图仅请求要显示行的单元格,而这一 点可通过tableView:cellForRowAtIndexPath:让表格视图实现。
对于表格中的任何给定行,取回toDoItems数组中的相应条目,然后将单元格的文本标签设定为项目的名称。
在表格中显示单元格
在项目导航器中,选择XYZToDoListViewController.m。
找到tableView:cellForRowAtIndexPath:数据源方法。模板实现是这样的:
该模板执行多个任务。它会创建一个变量来保存单元格的标识符,向表格视图请求具有该标识符的单元格,添加一个注释注明配置该单元格的代码应该写在哪里,然后返回该单元格。
要让此代码为您的应用程序所用,需要将标识符更改为您在串联图中设定的标识符,然后添加代码来配置该单元格。
将单元格标识符更改为您在串联图中设定的标识符。为了避免拼写错误,请将串联图中的标识符拷贝并粘贴到实现文件中。该单元格标识符行现在应该是这样的:
在 return 语句前,添加以下代码行:
您的tableView:cellForRowAtIndexPath:方法应如下图所示:
检查点:运行您的应用程序。您在loadInitialData中添加的项目列表应该在表格视图中显示为单元格。
如果无法将待办事项列表中的项目标记为已完成,那么这个待办事项列表还不够好。现在,让我们来添加这样的支持。一个简单的界面应该可以在用户轻按单元格时 切换完成状态,并在已完成的项目旁边显示勾号。幸运的是,表格视图附带了一些内建行为,您可以利用这些行为来实现这样的简单界面。需要注意的是,在用户轻 按单元格时,表格视图要通知它们的委托。所以我们的任务是写一段代码,对用户轻按表格中的待办事项这个操作作出响应。
您在串联图中配置XYZToDoListViewController时,Xcode 就已经让它成为表格视图的委托了。您要做的只是实现tableView:didSelectRowAtIndexPath:委托方法,使其响应用户轻按,并在适当时候更新您的待办事项列表。
选定单元格后,表格视图会调用tableView:didSelectRowAtIndexPath:委托方法,来查看它应如何处理选择操作。在此方法中,您需要编写代码来更新待办项目的完成状态。
将项目标记为已完成或未完成
在项目导航器中,选择XYZToDoListViewController.m。
将以下代码行添加到文件末尾,在@end行正上方:
请试着键入第二行,而不是拷贝和粘贴。您将发现代码补全是 Xcode 最节省时间的功能之一。当 Xcode 显示出可能的补全建议列表时,请滚动浏览列表,找到想要的建议,然后按下 Return 键。Xcode 会为您插入整行。
您想要响应轻按,但并不想让单元格保持选定状态。添加以下代码,让单元格在选定后立即取消选定:
在toDoItems数组中搜索相应的XYZToDoItem。
切换被轻按项目的完成状态。
告诉表格视图重新载入您刚更新过数据的行。
您的tableView:didSelectRowAtIndexPath:方法应如下图所示:
检查点:运行您的应用程序。您在loadInitialData中添加的项目列表在表格视图中显示为单元格。但当您轻按项目时,并没有任何反应。为什么呢?
原因是您尚未配置显示项目完成状态的表格视图单元格。要实现此功能,您需要回到tableView:cellForRowAtIndexPath:方法,并配置单元格以在项目完成时显示指示。
指示项目已完成的一种方式是在其旁边放置一个勾号。幸运的是,表格视图右边可以有一个单元格附属物。默认情况下,单元格中没有任何附属物;不过您可以进行更改,使其显示不同的附属物。其中的一个附属物就是勾号。您要做的是根据待办事项的完成状态,设定单元格的附属物。
显示项目的完成状态
前往tableView:cellForRowAtIndexPath:方法。
在设定单元格的文本标签的代码行下方添加以下代码:
您的tableView:cellForRowAtIndexPath:方法现在应如下图所示:
检查点:运行应用程序。您在loadInitialData中添加的项目列表在表格视图中显示为单元格。轻按项目时,其旁边应该出现一个勾号。如果您再次轻按同一项目,勾号会消失。
构建待办事项列表应用程序功能的最后一步是实现添加项目的能力。当用户在XYZAddToDoItemViewController场景的文本栏中输入项目名称,并轻按“Done”按钮时,您想要视图控制器创建一个新的列表项目并将其传递回XYZToDoListViewController,以显示在待办事项列表中。
首先,您需要拥有一个列表项目来进行配置。就像表格视图那样,视图控制器是将界面连接到模型的逻辑位置。为XYZAddToDoItemViewController添加一个属性来保存新的待办事项。
将 XYZToDoItem 添加到 XYZAddToDoItemViewController 类
在项目导航器中,选择XYZAddToDoItemViewController.h。
由于稍后需要从表格视图控制器访问列表项目,所以务必将其设为公共属性。这就是为什么要在接口文件XYZAddToDoItemViewController.h中声明它,而不在实现文件XYZAddToDoItemViewController.m中声明的原因所在。
将 import 声明添加到@interface行上方的XYZToDoItem类中。
将toDoItem属性添加到该接口。
要获得新项目的名称,视图控制器需要访问用户输入名称的文本栏。要实现此功能,请创建从XYZAddToDoItemViewController类到串联图中的文本栏的连接。
将文本栏连接到视图控制器
在大纲视图中,选择XYZAddToDoItemViewController对象。
点按窗口工具栏右上角的“Assistant”按钮,打开辅助编辑器。
右边的编辑器出现时,应该显示有XYZAddToDoItemViewController.m。如果未显示,请点按右边的编辑器中的文件名,并选取XYZAddToDoItemViewController.m。
辅助编辑器可让您一次打开两个文件,以便能够在它们之间执行操作。例如,在源文件中键入一个属性,而源文件的对象又在接口文件中。
在串联图中选择文本栏。
按住 Control 键从画布上的文本栏拖到右边编辑器中的代码显示窗口,到达XYZAddToDoItemViewController.m中的@interface行正下方时停止拖移。
在出现的对话框中,为“Name”栏键入“textField”。
让选项的其余部分保持不变。您的对话框应如下图所示:
点按“Connect”。
Xcode 会将必要的代码添加到XYZAddToDoItemViewController.m,用于储存指向文本栏的指针,并配置串联图来设置该连接。
另外,您需要知道何时创建项目。如果想要仅在“Done”按钮被轻按时才创建项目,那么请将“Done”按钮添加为 Outlet。
将“Done”按钮连接到视图控制器
在串联图中,打开辅助编辑器,将最右边的窗口设定为XYZAddToDoItemViewController.m。
在串联图中选择“Done”按钮。
按住 Control 键从画布上的“Done”按钮拖到右边的编辑器中的代码显示窗口,到达XYZAddToDoItemViewController.m中的textField属性正下方的行时停止拖移。
在出现的对话框中,在“Name”栏键入“doneButton”。
保持选项的其余部分不变。对话框应如下图所示:
点按“Connect”。
您现在有了识别“Done”按钮的方式。由于想在轻按“Done”按钮时创建一个项目,所以需要知道该按钮何时被按下。
当用户轻按“Done”按钮时,它会启动一个 unwind segue,返回到待办事项列表,这正是您在第二个教程中配置的接口。在 segue 执行前,系统通过调用prepareForSegue:,给所包含的视图控制器一次准备机会。这便是检查用户是否轻按了“Done”按钮的时候。如果是,将会创建一个新的待办事项。您可以检查轻按了哪一个按钮,如果是“Done”按钮,将会创建项目。
轻按“Done”按钮后创建项目
在项目导航器中选择XYZAddToDoItemViewController.m。
在@implementation行下方添加prepareForSegue:方法:
在此方法中,请查看“Done”按钮是否按下。
如果未按下就不存储项目,而是让方法返回但不执行任何其他操作。
查看一下文本栏中是否有文本。
如果有文本,将会创建一个新项目,并用文本栏中的文本为其命名。另外,请确保完成状态被设定为“NOfalse”。
如果没有文本,您就不需要存储项目,也不需要执行任何其他操作。
您的prepareForSegue:方法应如下图所示:
既然创建了一个新项目,那么就需要将该项目传递回XYZToDoListViewController,以便它可以将项目添加到待办事项列表。要完成此功能,请重新访问您在第二个教程中编写的unwindToList:方法。当用户轻按“Cancel”或“Done”按钮,XYZAddToDoItemViewController场景会在关闭时调用该方法。
就像作为 unwind segue 目标的所有方法一样,unwindToList:方法也采用 segue 作为参数。segue 参数是从XYZAddToDoItemViewController展开并返回到XYZToDoListViewController的过渡。由于 segue 是两个视图控制器之间的过渡,所以知道它的源视图控制器是XYZAddToDoItemViewController。通过向 segue 对象请求其源视图控制器,您可以用unwindToList:方法访问储存在源视图控制器中的任何数据。目前,您想要访问toDoItem。如果它是nil,则该项目并未创建。原因可能是文本栏没有文本,或者是用户轻按了“Cancel”按钮。如果toDoItem有值,则可以取回该项目,再添加到toDoItems数组,并通过重新载入表格视图中的数据将其显示在待办事项列表中。
储存并显示新项目
在项目导航器中,选择XYZToDoListViewController.m。
将 import 声明添加到@interface行上方的XYZAddToDoItemViewController类中。
找到您在第二个教程中添加的unwindToList:方法。
在此方法中,取回源视图控制器XYZAddToDoItemViewController,即您要展开的控制器。
取回控制器的待办事项。
这就是轻按“Done”按钮时创建的项目。
看看这个项目是否存在。
如果是nil,可能是“Cancel”按钮关闭了屏幕,或者是文本栏中没有文本,因此不必存储该项目。
如果项目存在,请添加到toDoItems数组。
重新载入表格中的数据。
因为表格视图不会跟踪其数据,所以数据源(在这个程序中是表格视图控制器)有责任通知表格视图何时有新数据供其显示。
您的unwindToList:方法应如下图所示:
检查点:运行您的应用程序。现在,当您点按添加按钮 (+) 并创建一个新项目时,它应该会显示在待办事项列表中。恭喜您!您已经创建了一个应用程序,它能接收用户的输入,将其储存在对象中,并在两个视图控制器之间传递该对象。对于基于串联图的应用程序,这是在场景之间移动数据的基础。
您差不多完成了开发 iOS 应用程序的入门之旅。最后一部分更详细地讲述了如何查询文稿材料,并且为您学习创建更高级的应用程序给出了一些后续建议。