苹果的文档指出:
如果您之前注册了具有相同重用标识符的类或 nib 文件,则您在参数中指定的类将替换旧条目。如果您想从指定的重用标识符中取消注册该类,
cellClass
请指定nil
。cellClass
据我所知,这似乎不正确。
下面简单的示例代码演示了这个问题。基本上,我有一个可以改变填充值的滑块。当填充值发生变化时,它应该用重用标识符重新注册(替换旧条目)该类,并重新加载表格以显示新的填充值:
import UIKit
import SnapKit
extension String {
static let kPadding = Self("padding")
static let cellId = Self("cell")
}
class ViewController: UIViewController, UITableViewDataSource {
let tableView = UITableView(frame: .zero, style: .plain)
override func viewDidLoad() {
super.viewDidLoad()
registerCell()
tableView.dataSource = self
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.horizontalEdges.top.equalToSuperview()
}
let slider = UISlider()
slider.isContinuous = false
slider.minimumValue = 0
slider.maximumValue = 100
slider.value = UserDefaults.standard.float(forKey: .kPadding)
slider.addTarget(self, action: #selector(sliderChanged(slider:)), for: .valueChanged)
view.addSubview(slider)
slider.snp.makeConstraints { make in
let padding = 15.0
make.horizontalEdges.bottom.equalTo(view.safeAreaLayoutGuide).inset(padding)
make.top.equalTo(tableView.snp.bottom).offset(padding)
}
}
@objc func sliderChanged(slider : UISlider) {
print("sliderChanged: \(slider.value)")
UserDefaults.standard.set(slider.value, forKey: .kPadding)
registerCell()
tableView.reloadData()
}
func registerCell(){
tableView.register(Cell.self, forCellReuseIdentifier: .cellId)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: .cellId) as! Cell
cell.label.text = "Hello \(indexPath.row)"
return cell
}
}
class Cell: UITableViewCell {
let label = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
label.font = UIFont.systemFont(ofSize: 34, weight: .bold)
contentView.addSubview(label)
label.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UserDefaults.standard.float(forKey: .kPadding))
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
然而,事实似乎并非如此。它继续使用之前的填充,并且旧条目不会被替换。
我是否误解了 Apple 文档的内容?
虽然发布的示例代码一开始就很糟糕,但文档似乎也不正确。或者说,它根本就不清楚应该发生什么。
让我们看一下这个例子(不使用 SnapKit)...
我们将创建一个“基础”细胞类:
和两个子类 -
CellA
和CellB
(不同的颜色可以很容易地看出哪一个正在被使用):通过对单元格进行子类化,我们可以使用
cellForRowAt
不带 if/else 的情况:如果我们
CellA
这样注册:典型的布局如下:
或者,如果我们
CellB
像这样注册:我们得到这个输出:
因此,让我们在底部添加一个按钮来切换注册:
启动时,它看起来像这样:
如果我们点击“切换到...”按钮,我们将
tableView.register(...)
再次调用,在CellA
和 CellB, and call
reloadData() 之间切换(相关代码):那么,点击按钮我们能得到什么呢?
显然,由于“真”已变为“假”,因此单元格已重新加载......但它们仍在出队
CellA
!那么,让我们向上滚动几行:
表格视图必须在 上创建 3 个新单元格
.dequeueReusableCell
,并按预期使用CellB
类...但从“行:14”开始,它又回到了重新使用CellA
!如果我们继续滚动:
我们看到表格视图现在有 11 个
CellA
实例,CellB
其“可重用队列”中有 3 个实例......并且我们当前注册了哪个类并不重要。另请注意...文档指出:
但是,如果我们进行初始加载,则执行以下代码:
cellForRowAt
将被每个可见单元格调用并且它们仍将更新!如果我们尝试滚动,应用程序将崩溃
cellForRowAt
并显示“致命错误:在解开可选值时意外发现 nil”滚动时崩溃是有道理的,尽管重新加载已经排队的单元格却没有道理。