繁体   English   中英

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

[英]Wrong IndexPath after insertion/deletion of cell from 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传递到viewModel.updateSelectionState将是错误的(将电池删除之前等于值)。

我想我知道为什么

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

解决该问题的方法

* 1

询问里面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)
}

*第二

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

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

感谢您的任何建议。

只有一种方法才能满足第一个条件,以保持动画流畅 deleteRows中断动画后立即调用reloadData

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

不要使用重新加载特定的行,因为删除行时索引已更改,并且删除行后它会删除动画,这就是为什么需要重新加载tableview.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()
    })

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM