ios - 从 UITableView 插入/删除单元格后的 IndexPath 错误
问题描述
代码
VC问题部分的要点:
// Part of VC where cell is setting
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(for: indexPath) as Cell
let cellVM = viewModel.cellVM(for: indexPath)
cell.update(with: cellVM)
cell.handleDidChangeSelectionState = { [weak self] selected in
guard
let `self` = self
else { return }
self.viewModel.updateSelectionState(selected: selected, at: indexPath)
}
return cell
}
// Part of code where cell can be deleted
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let deleteAction = UITableViewRowAction(style: .destructive, title: "delete".localized, handler: { [weak self] _, indexPath in
guard let self = self else { return }
self.viewModel.delete(at: indexPath)
tableView.deleteRows(at: [indexPath], with: .left)
})
return [deleteAction]
}
问题
当单元格被删除并且之后handleDidChangeSelectionState
将涉及然后indexPath
传入viewModel.updateSelectionState
将是错误的(将等于单元格删除之前的值)。
我想我知道为什么
IndexPath
是一个结构,因此handleDidChangeSelectionState
保留当前值(不是实例)的副本。原始值的任何更新都不会更新捕获的副本。tableView.deleteRows
不会重新加载 tableview 的数据源,所以cellForRowAt
不会召回。这意味着handleDidChangeSelectionState
不会捕获更新的副本。
我解决这个问题的方法
* 第一个
询问里面的 indexPath 值handleDidChangeSelectionState
:
cell.handleDidChangeSelectionState = { [weak self, weak cell] selected in
guard
let `self` = self,
let cell = cell,
// now I have a correct value
let indexPath = tableView.indexPath(for: cell)
else { return }
self.viewModel.updateSelectionState(selected: selected, at: indexPath)
}
* 第 2 名
每次删除后执行reloadData()
:
let deleteAction = UITableViewRowAction(style: .destructive, title: "delete".localized, handler: { [weak self] _, indexPath in
guard let self = self else { return }
self.viewModel.delete(at: indexPath)
tableView.deleteRows(at: [indexPath], with: .left)
// force to recall `cellForRowAt` then `handleDidChangeSelectionState` will capture correct value
tableView.reloadData()
})
问题
哪种方法更好?
我想要:
- 保持流畅的动画(感谢
tableView.deleteRows(at: []
) - 找到更好的性能(我不确定哪个更优,
reloadData()
或者indexPath(for cell:)
)
也许有更好的第三种方法。
感谢您的任何建议。
解决方案
只有第一种方法满足保持流畅动画的第一个条件。在中断动画reloadData
后立即调用。deleteRows
而且调用indexPath(for: cell)
肯定比重新加载整个表视图更便宜。
推荐阅读
- java - OpenTok 服务器设置
- java - 将 Java 派生的 Thread 类添加到 Vector 失败
- reactjs - 使用保持某些状态的钩子时如何使组件重新渲染
- prometheus - 当所有抓取作业都关闭时,Prometheus 中的主机关闭警报
- xml - 如何从具有命名空间的 XML 中获取节点
- c# - c# 多维数组迭代
- batch-file - 如何在 Windows 10 上运行带有批处理文件的 ipython notebook?
- scala - 理解抽象方法覆盖具体方法时的逻辑
- envoyproxy - Envoyproxy 1.11 > 1.12 无法使用 RDS 找到 CORS
- java - Pdfview 库未使用 url 加载