我们一般创建 UITableViewCell
会这么写:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
}
return cell!
}
如果需要注册,则这么写:
// 注册 cell
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
有区别吗?一个最明显的区别就是,一个是可选的 ( Optional
),一个是一定有值的;还有就是,使用第二种方法时,如果事先未注册 Cell,则会引发崩溃,目的是为了提醒开发者:你忘记注册了!
基于以上两点,官方建议使用第二种方法创建 UITableViewCell
。
扩展 UITableView
为了解决每次创建 Cell 时都要手动的给它注册一遍的问题,需要给它扩展一个方法,怎么做呢?
我们的目的是不用手法注册,也就意味着第二种方法已经行不通(需要注册),那我们把目光聚焦在第一个方法上。
怎么做呢?解决重复的代码是第一步,假如每次创建 Cell 都要写这么一个判断,那不是烦死了?为了能够重用,我们可以封装一下,比如:
func createCell(tableView: UITableView, style: UITableViewCellStyle, reuseIdentifier: String) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier)
if cell == nil {
cell = UITableViewCell(style: style, reuseIdentifier: reuseIdentifier)
}
return cell!
}
然后我们使用这个自定义创建 Cell 的方法:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = create(tableView: tableView, style: .default, reuseIdentifier: "Cell")
return cell
}
然则如果把方法封装在 tableView 的 extensions
里面,则更好一些,因为该方法是基于 tableView 来调用的,可以这样:
public extension UITableView {
func dequeueReusableCell(style: UITableViewCell.CellStyle = .default, identifier: String? = nil) -> UITableViewCell {
var cell = dequeueReusableCell(withIdentifier: identifier)
if cell == nil {
cell = UITableViewCell(style: style, reuseIdentifier: identifier)
}
return cell!
}
}
来试一下:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(style: .default, identifier: "Cell")
return cell
}
这样一来感觉就好很多了,看起来和调用系统的方法差不多。但是还有两个问题:
- 创建不同类型的 Cell
在这里我们使用的是UITableViewCell
类型创建 Cell,如果需要创建不同的类型怎么办? - 创建来自 Nib 的 Cell
在这里我们只使用代码的方式创建 Cell,如果要创建来自 Nib 的 Cell 怎么办?
我们来逐一解决这两个问题。
创建不同类型的 Cell
解决第一个问题的一个有效的方法就是使用泛型,我们在方法里面新增一个表示 UITableViewCell
的泛型类。如下所示:
func dequeueReusableCell(cellType: T.Type = T.self, style: UITableViewCell.CellStyle = .default, identifier: String? = nil) -> T {
var cell = dequeueReusableCell(withIdentifier: identifier ?? "\(cellType)") as? T
if cell == nil {
cell = T(style: style, reuseIdentifier: identifier ?? "\ cellType)")
}
return cell!
}
来分析一下这个方法,首先方法添加了一个泛型
,目的是为了能够创建不同类型的 Cell 并返回(注意,返回值也是 T
)。
然后接下来的一行代码是:
// 使用重用标识符从缓存池中获取 Cell
var cell = dequeueReusableCell(withIdentifier: identifier ?? "\(cellType)") as? T
该方法的意思注释已经说了:是使用重用标识符从缓存池中获取 Cell。那么这个重用标识符应该传什么呢?你当然可以传方法参数里面的 identifier
,但一个更好的方式是使用 Cell 的类名来作为它的重用标识符。综上所述,在没有指定 identifier
参数的情况下使用 Cell 的类名来作为它重用标识符,它应该传:identifier ?? "\(cellType)"
。
接下来的一段代码是:
if cell == nil {
cell = T(style: style, reuseIdentifier: identifier ?? "\(cellType)")
}
在判断 cell
为空时执行,和普通的创建方式一样,只是这里使用了 T
代替 UITableViewCell
,泛型 T
指定了它应该是 UITableViewCell
类,所以本质上它跟 UITableViewCell
类是一样的。
这样一来,在不使用 Nib 的情况下,这个方法完全没有问题了。来试一下:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 不指定类型,默认返回 `UITableViewCell`。
// let cell = tableView.dequeueReusableCell()
// 返回 MyTableViewCell
// let cell = tableView.dequeueReusableCell() as MyTableViewCell
// 返回 MyTableViewCell
// let cell: MyTableViewCell = tableView.dequeueReusableCell()
// 返回 MyTableViewCell
let cell = tableView.dequeueReusableCell(cellType: MyTableViewCell.self)
return cell
}
创建来自 Nib 的 Cell
为了能够区别于代码创建的方式,我们在定义一个专门用来处理创建 Nib Cell 的方法,因为如果都放在一起,逻辑处理起来会比较复杂。
func dequeueReusableNibCell(cellType: T.Type = T.self, nibName: String? = nil) -> T {
var cell = dequeueReusableCell(withIdentifier: nibName ?? "\(cellType)") as? T
if cell == nil {
cell = UINib(nibName: nibName ?? "\(cellType)", bundle: nil).instantiate(withOwner: nil).last as? T
}
return cell!
}
其实情况差不多,标识符参数由 identifier
变成了 nibName
,UITableViewCell
的创建方式变成了使用 UINib
来创建,最后返回这个 cell
。
于是你可以像代码创建的方式一样愉快的创建 Nib 中的 UITableViewCell
了。
重要:
使用 Nib 创建方式,xib 里面的 Cell 标识符务必要和方法里面的重用标识符一致,否则很有可能会引发崩溃或未知错误。