UITableView 会显示一列 UITableViewCell 对象。对于大部分应用 , 基础 UITableViewCell 的 textlabel 、 detailTextlabel 和 image View 就够用了。但是,如果要显示更多内容,或者定制布局,就需要创建 UITableViewCell 子类了。
向 UITableViewCell 子类的 contentView 添加子视图,可以定制界面。
注意:不要直接向UITableViewCell 添加子视图,而要添加到 contentView 上,因为有时 UITableViewCell 会改变 contentView 的大小 。
例如:当 UITableView 进入编辑模式时,为了显示删除按钮, contentView 的大小就会改变。如果直接向 UITableViewCell 添加子视图,删除按钮就可能被挡住。进入编辑模式时, UITableViewCell 是不能调整大小的(它需要保待与 UITableView 一样宽),但是 contentView 可以改变大小。

- 创建一个叫 ItemCell 的 Swift 文件。
- 使用 storyboard 配置 UITableViewCell 子类。
打开 Main.storyboard,在文件大纲中选择 UITableViewCell 。 打开属性检视面板,把
Style(样式)改为Custom(定制),然后把 Identifier(标识)改为 ltemCell 。接着,打开标识检视面板。 在Class输入框中输入ItemCell。把 prototype cell 的高度修改为 65 点 。可以直接在画布上修改,也可以选中 prototype cell 后在尺寸检视面板中修改 Row Height(行高)。
拖三个 UILabel 对象到storyboard并添加约束。
- 打开 ItemCell.swift,添加三个插座变量的属性并与storyboard的Label进行关联。
注意:ItemCell 的插座变量在子类视图中。 可以打开 Main.storyboard , 按住 Control 并点击文件大纲中的 ItemCell 。
class ItemCell: UITableViewCell{
@IBOutlet var nameLabel: UILabel!
@IBOutlet var serialNumberLabel: UILabel!
@IBOutlet var valueLabel: UILabel!
}

- 在 ItemViewController 的
tableView(_: cellForRowAtIndexPath: ) 方法中, UITableView 的每行都会获得一个 ItemCell 对象。现在使用了自定义的 UITableViewCell 子类,因此 UITableView 需要知道每行的高度。有几种方法可以完成这个任务 ,最简单的是给 UITableView 的rowHeight 属性设置一个常量值。本章后面会介绍其他方法。
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 65
}
- 现在已在 UITableView 中注册过 ItemCell 了(在 storyboard 的原型 Cell 中),因此可 以通过 “ ItemCell" 标识来获得 ItemCell 了 。在 ItemsViewController.swift 中修改 tableView(_: cellForRowAtIndexPath: ) ,代码如下:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = UITableViewCell(style: .value1, reuseIdentifier: "UItableViewCell")
//let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath) //创建一个新的 UITableViewCell 对象或重用一个 UITableViewCell 对象
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
let item = itemStore.allItems[indexPath.row] //将 tableview 中第n行的文字设置为第n个 item 对象的名字。
// cell.textLabel?.text = item.name
// cell.detailTextLabel?.text = "$\(item.valueInDollars)"
cell.nameLabel.text = item.name
cell.serialNumberLabel.text = item.serialNumber
cell.valueLabel.text = "$\(item.valueInDollars)"
return cell
}
- 以上代码首先更新了重用标识 ,指向了新的对象,然后,在方法末尾为 ItemCell 的每个 标签设置合适的值。
动态计算Cell高度
使用自动布局来完成这个任务。 ItemCell 需要一个明确决定高度的约束,但是现在 ItemCell 还没有这个约束,读者需要在两个标签之间添加一个固定间距的约束。
打开 Main.storyboard 。按住 Control,从 nameLabel 拖到 serialNumberlLabel后选择
Vertical Spacing (竖直间距)。下面打开 ItemsViewController.swift,更新 viewDidLoad() 方法 ,让UITableView根据约束计算 ItemCell 的高度。
override func viewDidLoad() {
super.viewDidLoad()
//tableView.rowHeight = 65
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 65 //设置 estimatedRowHeight 属性可以让这些计算延后,直到用户滑动 UITableView 时才去计算。
}
- UITableViewAutomaticDimension 是 rowHeight 属性的默认值,虽然没有必要设置, 但是加上可以让代码更容易理解。
动态类型
支持动态类型的应用会自动缩放字体。本节将会让 ItemCell 支持动态类型。
打开 Main.storyboard 。下面让标签不再使用固定的字体,而是使用文字样式。选中 nameLabel 和 valuelabel 后打开属性检视面板。点击 Font(字体)右边的文字图标,在 Font 中选择 Text Styles-Body。对 serialNumber 执行相同的操作,选择 Caption 1 文字样式。
编译并运行应用(无论是使用任务切换器还是 Home 键切换回应用,都不会看到刚才的修改。下一 节会修复这个问题),再向 UITableView 中添加一些 Item , 就可以看到新的小文字样式了 。
响应用户的修改
当用户改变首选字体大小回到应用时, UITableView 应该重新加载数据。很不幸,标签并不知道新选择的字体大小,需要手动更新标签来修复这个问题 。
- 打开 ItemCell. swift,
重载 awakeFromNib()方法来让标签自适应字体大小。
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.adjustsFontForContentSizeCategory = true
serialNumberLabel.adjustsFontForContentSizeCategory = true
valueLabel.adjustsFontForContentSizeCategory = true
}
当 ItemCell 从 storyboard 文件中加载完成后,就会调用 awakeFromNib() 方法。调用这个方法时,所有插座变量都已经有值了 。
编译并运行应用,然后添加一些 Item。 到设置应用中把首选字体设置为最大。与之前的不同,现在通过任务切换器或者 Home 键切换回 Homepwner,UITableView 会使用新的首选字体更新界面。
L