从UITableView插入/删除单元格后,IndexPath错误

时间:2019-02-27 08:49:33

标签: ios swift uitableview

代码

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的{​​{1}}将是错误的(等于删除单元格之前的值)。

我想我知道为什么

  1. viewModel.updateSelectionState是一个结构,因此IndexPath保留当前值的副本(不是实例)。原始值的任何更新都不会更新捕获的副本。
  2. handleDidChangeSelectionState将不会重新加载tableview的数据源,因此tableView.deleteRows将不会重新调用。这意味着cellForRowAt将不会捕获更新的副本。

我要解决该问题的方法

* 1st

询问handleDidChangeSelectionState中的indexPath值:

handleDidChangeSelectionState

* 2nd

每次删除后,执行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) }

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()哪个更好)

也许有更好的第三种方法。

谢谢您的任何建议。

2 个答案:

答案 0 :(得分:1)

只有第一个方法满足保持平滑动画的第一个条件。在reloadData之后立即调用deleteRows会中断动画。

调用indexPath(for: cell)肯定比重新加载整个表视图便宜。

答案 1 :(得分:0)

请勿使用重新加载特定的行,因为删除行时索引已更改,因此删除行后它将删除动画,这就是为什么您需要重新加载tableview.reloadData()的原因,这是一个更好的选择。 / p>

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()
    })