点击UICollectionViewCell内按钮删除单元格出现索引越界错误如何解决?
问题根因
你遇到的报错本质是两个核心问题导致的:
- cell复用过程中,删除按钮绑定事件时捕获的
row是cell第一次渲染时的固定值,后续列表发生删除/新增操作后,剩余cell的实际索引已经更新,但绑定事件里存储的还是旧索引,自然会出现索引越界、删错行的问题。 - 你同时使用RxSwift的自动数据源绑定和手动调用
deleteItems操作列表,两者更新逻辑冲突:bind(to: rx.items)本身会自动监听数据源变化更新列表,手动调用删除cell的方法会和自动更新逻辑重复,导致系统校验数据源和列表数量不匹配报错。
修复步骤
1. 移除手动操作UICollectionView的冗余代码
删掉performBatchUpdates、deleteItems、reloadItems相关的代码,所有列表更新交给RxSwift的自动绑定处理,避免操作冲突。
2. 点击删除按钮时实时计算当前cell的真实索引
不要用绑定时的固定row,改成点击按钮时动态获取按钮所在cell的当前索引,保证操作的是正确的行:
// rx.items绑定闭包里的删除逻辑 cell.deleteBtn.rx.controlEvent(.touchDown).subscribe(onNext: { [weak self] _ in guard let self = self, let viewModel = self.viewModel else { return } // 动态获取cell当前的真实indexPath guard let indexPath = self.formCV.indexPath(for: cell) else { return } var currentData = viewModel.form.value // 加安全校验避免越界 guard indexPath.row < currentData.count else { return } currentData.remove(at: indexPath.row) viewModel.form.accept(currentData) self.contentViewHeightConstraints.constant -= 779 // 重点:订阅放到cell的disposeBag里,不要用控制器的全局disposeBag }).disposed(by: cell.disposeBag)
3. 修复cell复用导致的订阅泄漏问题
给AddFormCell添加专属的disposeBag,每次cell复用时销毁之前的旧订阅,避免同一个cell绑定多个删除事件:
class AddFormCell: UICollectionViewCell { // 每个cell单独维护自己的订阅容器 var disposeBag = DisposeBag() override func prepareForReuse() { super.prepareForReuse() // 复用前重置,销毁之前所有的旧订阅 disposeBag = DisposeBag() } // UI初始化只做一次,放到awakeFromNib里,不要每次复用都重新初始化 override func awakeFromNib() { super.awakeFromNib() initCellView() iniStatesList() } }
4. 优化数据源绑定逻辑
self.viewModel!.form.asObservable() .bind(to: self.formCV!.rx.items(cellIdentifier: "AddFormCell", cellType: AddFormCell.self)) { row, item, cell in // 不要用+=,复用的时候会累加文字 cell.formCountLbl.text = "表单 \(row + 1)" cell.deleteBtn.isHidden = self.viewModel?.form.value.count ?? 1 <= 1 // 这里写上面的删除按钮绑定逻辑 } .disposed(by: self.disposeBag)
额外注意点
- 你新增表单的逻辑里,取数组用的是
viewModel?.shops.value,更新的时候用的是viewModel?.form.accept,检查是不是属性名写错了,这里很可能是bug来源。 - 全局的
self.data没有必要使用,每次都直接从viewModel.form.value取最新的数据源即可,避免全局变量和ViewModel里的数据源不一致。 - 尽量不要强制解包
viewModel、formCV这类可选值,用guard let做安全校验,避免意外崩溃。
内容的提问来源于stack exchange,提问作者user12401635
相关产品推荐
相关产品推荐

