You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 00:39:00