第六章 创建一个基于Table的简单App(二)
专题目录:
UITableView和协议
在我们使用iOS SDK提供的基础类之前我要稍作介绍。这些基础类被组织在一起并称为框架(frameworks)。UIKit框架就是使用最为广泛的框架之一。
UIKit框架提供的类可以让用户构建和管理用户界面(UI)。所有storyboard的对象库中可以获取的对象都由这个框架提供。你第一个App中使用的Button对象和现在使用的Table View对象都是这个框架中的。当我们说TableView时,其实真正对应的类是UITableView。你可以在对象库中点击任何对象,在弹出的窗口中可以看到真正的类名及介绍。
这样你对Table View和UITableView之间的关系有所了解了。我们接下来写一些代码把数据插入到表格里。在工程导航窗口选择ViewController.swift,双击打开代码编辑界面。在UIViewControlller后面添加UITableViewDataSource和UITableViewDelegate。(UITableView需要这两种协议)
当你在UIViewController后面添加代码时,Xcode马上会探测到错误。当Xcode发现问题时会显示一个红色叹号标识。点击编辑区域左边的小叹号,Xcode将会高亮显示有问题的代码同时显示一行消息告诉你出了什么问题。这条消息可以告知你出了什么问题但是不会提供解决办法。
那么“ViewController does not conform to protocol UITableViewDataSource”是什么意思?
在Swift中UITableViewDelegate和UITableViewDataSource叫做协议。为了在table view中显示数据,我们必需遵从一系列协议中定义的需求并且提供一个对象(这里就是ViewController类)实现所有需要委托实现的方法。
现在可能有些令人困惑。这些是什么协议?等等,什么是协议?
好了,假设你开了一家新的公司。你雇用了一个美术设计师设计你的公司logo。他是一个训练有素的设计师,有能力设计任何logo。但是他不能立刻开始logo设计。最起码,你需要告诉它一些基本的信息如公司名字,你偏爱的颜色。但是,你工作非常的忙。你委托你的私人助理完成这项提供信息的任务。
在iOS编程中,UITableView类就像那个美术设计师。UITableView足够的灵活可以在表格中显示多种多样的数据(例如,图片,文字)。你可以显示一系列国家名或其他交互名。在这个例子里,我们使用缩略图一起显示一系列饭店名。
UITableView需要一位代理来提供一些基本的信息:
1在Table View中你想要显示几行?
2表中的数据是什么?例如,在第2行你想要显示什么?在第5行你想要显示什么?
ViewController扮演了上面提到的私人助理的角色,给UITableView提供需要的信息。
但是我们如何告诉UITableView哪些数据要显示呢?UITableViewDataSource(表格数据源协议)协议就是关键。它就是数据和table view之间的联系。表格数据源协议中定义了一系列你必须实现的方法。表格数据源协议中必须要实现的两个方法是:
tableView(_:numberOfRowsInSection:)
tableView(_:cellForRowAtIndexPath:)
你需要一个实现上面方法的对象,这样UITableView才知道需要显示多少行以及每行显示什么数据。表格数据源协议同样还有一些可选的方法,不过这里我们不讨论。
另一方面UITableViewDelegate协议(表格代理协议)处理UITableView的显示部分。表格代理协议里所有的方法均是可选的。这些方法可以让你管理表格的高度,配置表头和表脚,对单元格进行排序等等。在这个例子里我们不会修改这些方法。我们将在后面的章节里修改这些方法。
好了,了解了关于协议的基本概念,让我们继续编写App代码。选择ViewController.swift然后声明一个变量存储表格数据。将这个变量命名为”restaurantNames”因为我们将用它存储许多餐馆名。
在这个例子里我们使用数组来存储表格数据。在Swift中声明数组的语法类似Objective-c。这些餐馆名放在一对中括号之间并且用逗号隔开。
为初学者介绍一下数组:数组是计算机编程里的基本数据结构。你可以认为数组是一个数据集合。考虑上面代码中的食谱数组,它代表一串字符串集合。你可以想象数组是这样的
数组中的每一个元素由下标标示或者访问。拥有10个元素的数组下标为0-9。这意味restaurantNames[0]返回的是数组的第一元素。
接下来,我们实现UITableViewDataSource协议必须的两个方法。
第一个方法用来通知table view一节(table view可以有好几个节,不过这里默认只设定一个节)中有多少行。要获得数组restaurantNames中的元素个数只要调用count方法就可以了。
第二个方法在表格的每一行被显示的时候都要调用一次。使用indexPath对象我们可以获取当前的行(indexPath.row)。我们调用改方法从restaurantNames数组中获取元素并且在text label(cell.textLabel.text)中显示出来。
ok,那会有人问代码中第二行dequeueReusableCellWithIdentifier是什么意思?
dequeueReusableCellWithIdentifier方法用指定的单元格标识(就是我们在前面storyboard中定义的“cell”)从队列中获取可重用的表格单元格。
你肯定希望你基于表格的App可以在处理成千上万行数据时依然保持快速响应。如果你不重用单元格而是重新申请单元格你的App将会使用非常多的内存,导致用户下拉表格时App反应迟缓。我们要记住每次单元格申请都需要代价,特别是在短时间突然分配大量的单元格。
iPhone真正的屏幕空间有限。即使是你的App需要显示1000条记录,但屏幕只能一次显示10行表格。所以为什么App不只分配10个单元格并且重用它们。这样会节省大量的内存并且让表格工作起来更有效。基于上面的原因,我们选择重用单元格而不是重新申请新的单元格。
好,现在我们在点击“Run”按钮测试你的App。哦!App仍然显示和上次一样的空白表格。
为什么table view仍然不显示我们希望的内容?我们已经写了显示表格数据的代码和实现了需要的方法。但是为什么table view不显示希望的内容?
因为,还有一件事情没有做!