如何正确调用tableView.selectRow以规避UITableViewAlertForCellForRowAtIndexPathAccessDuringUpdate警告
这个警告的核心原因很明确:你在configureCellSelectionState里调用的tableView.selectRow(at:animated:scrollPosition:)和tableView.deselectRow(at:animated:)会触发UITableView内部去请求单元格(间接调用cellForRowAt),而当表格正处于更新流程中(比如切换编辑状态、刷新行的动画过程),这种调用是被系统禁止的,于是就抛出了这个断言警告。
要解决这个问题,我们需要避开调用表格的select/deselect方法,转而直接控制单元格的选中状态,同时手动维护选中状态的数据源。下面是具体的修改方案:
1. 修改选中状态配置逻辑,直接操作Cell的选中属性
把原来的configureCellSelectionState改成直接设置cell的isSelected属性,绕过表格的内部更新流程:
private var rowSelectionState = [IndexPath: Bool]() private func configureCellSelectionState(for cell: UITableViewCell, at indexPath: IndexPath) { guard tableView.isEditing else { cell.isSelected = false return } // 直接设置cell的选中状态,避免触发表格的内部回调 cell.isSelected = rowSelectionState[indexPath] ?? false }
2. 在willDisplay中调用配置方法
继续在willDisplay里调用这个方法,确保单元格显示时状态正确:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { configureCellSelectionState(for: cell, at: indexPath) }
3. 优化选中/取消选中的逻辑
在didSelectRowAt和didDeselectRowAt中,除了维护rowSelectionState,还可以直接更新当前cell的状态,保证操作的实时性:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if tableView.isEditing { rowSelectionState[indexPath] = true // 直接更新当前cell,无需等待表格回调 if let cell = tableView.cellForRow(at: indexPath) { cell.isSelected = true } } } override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { if tableView.isEditing { rowSelectionState[indexPath] = false if let cell = tableView.cellForRow(at: indexPath) { cell.isSelected = false } } }
4. 编辑状态切换时同步单元格状态
关闭编辑模式时,清空选中状态的同时,手动更新所有可见单元格的选中状态:
override func setEditing(_ editing: Bool, animated: Bool) { super.setEditing(editing, animated: animated) if !editing { rowSelectionState.removeAll() // 刷新可见单元格,确保选中状态被取消 tableView.indexPathsForVisibleRows?.forEach { indexPath in if let cell = tableView.cellForRow(at: indexPath) { cell.isSelected = false } } } }
5. 可选:同步系统选中行的获取逻辑
如果你的代码中有依赖tableView.indexPathsForSelectedRows的地方,可以重写这个属性,让它返回我们手动维护的选中行:
override var indexPathsForSelectedRows: [IndexPath]? { return rowSelectionState.filter { $0.value }.map { $0.key } }
这样修改后,既可以实现多行选择的功能,又不会触发系统的断言警告——因为我们直接操作cell的属性,没有触发表格内部的更新流程,也就不会在表格更新期间去请求单元格了。
内容的提问来源于stack exchange,提问作者jims1103

