如何解决iOS带自定义单元格的TableView滑动删除仅清数据不删行问题
滑动删除TableView行不生效的问题定位与修正
现有实现的核心缺陷
- 数据模型和TableView的索引逻辑不匹配
目前使用键为行号的字典分别存储任务名称、选择的时间,同时单独维护taskCount变量作为行数返回值。这类字典是无序结构,删除某条数据后并不会自动重排剩余元素的key:比如初始3行对应的字典key为0/1/2,删除索引1的行后,剩余的有效数据对应的key还是0和2,但TableView后续会按连续的0/1索引去数据源取数,直接造成数据错位、取值为nil的问题。 - 删除操作的流程存在逻辑冲突
在调用deleteRows(at:with:)触发删除动画后,立刻执行了taskList.reloadData()。这个操作会强制TableView完全按照当前数据源重新渲染全列表内容,直接打断TableView正在处理的删除事务,触发系统的数据源一致性校验错误,最终回滚删除操作,表现为界面上的行没有被移除。 - 适配SelfSizedTableView的逻辑缺失
自定义自适应高度TableView没有在删除操作完成后触发视图高度重算逻辑,也会导致界面状态和实际数据源不匹配。
修正后的可运行实现
首先替换原有的字典类数据源为有序数组,从根源上保证数据源索引和TableView行号完全对齐:
// 定义统一的任务数据模型,替代原来两个独立的存储字典 struct TaskItem { var taskName: String var taskTime: String } class TaskListViewController: UIViewController { @IBOutlet weak var taskList: SelfSizedTableView! // 用有序数组作为唯一数据源,替代原有的taskCount、taskName、taskTime变量 private var taskItems = [TaskItem]() override func viewDidLoad() { super.viewDidLoad() // 初始化首行默认数据,替代原taskTime[0]的赋值逻辑 taskItems = [TaskItem(taskName: "", taskTime: "Set time")] taskList.delegate = self taskList.dataSource = self } } extension TaskListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 行数直接返回数据源数组的元素个数,不需要单独维护计数变量 return taskItems.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskCell cell.delegate = self // 按行索引从有序数组中取对应配置cell的内容 let currentTask = taskItems[indexPath.row] cell.nameField.text = currentTask.taskName cell.timeButton.setTitle(currentTask.taskTime, for: .normal) return cell } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { // 只响应删除操作 guard editingStyle == .delete else { return } // 第一步:先从数据源移除对应索引的元素,保证数据源和界面行索引完全同步 taskItems.remove(at: indexPath.row) // 第二步:执行行删除动画,不要在这之后调用全量reloadData tableView.deleteRows(at: [indexPath], with: .fade) // 适配SelfSizedTableView:删除完成后触发布局更新,重算视图总高度 tableView.layoutIfNeeded() } }
实现注意事项
- 禁止在
deleteRows操作完成后立刻调用reloadData,这个操作会打断TableView的批量更新事务,触发系统的内部一致性校验报错,直接回滚删除逻辑。 - 自定义Cell里的输入框编辑回调、按钮选择的时间更新,要直接同步修改对应数组索引位置的
TaskItem属性,不要继续用字典做中间存储,避免出现输入内容和行位置错位的问题。 - 新增行的操作也要同步在数组的对应索引位置插入元素,再调用TableView的
insertRows方法,不要直接修改计数变量后调用reloadData。
内容的提问来源于stack exchange,提问作者Nadia Siddiqah
相关产品推荐
相关产品推荐

