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

iOS15+滚动时嵌套UICollectionView的UITableViewCell消失报错求解

问题根因

该断言触发的核心原因是iOS 15.x版本新增了UITableView预取缓存的一致性校验,结合当前代码的实现缺陷触发:

[Assert] UITableView internal inconsistency: cell prefetched for IP(0,6) already stored for IP(0,5).

现有实现存在两个直接触发问题的错误:

  1. 所有cell内部嵌套的UICollectionView都将delegate、dataSource指向外层控制器,多个collectionView共享同一个代理对象,cell复用时状态完全错位
  2. 每次cell出队时都重复执行collectionView的cell注册、代理赋值、reload操作,破坏了UITableView复用池和预取缓存的索引映射关系,滚动时预取缓存记录的indexPath和实际返回的cell不匹配,就会触发断言,严重时系统直接丢弃对应位置的cell导致列表项消失。
    该问题仅在开启预取的iOS 15+环境触发,iOS 13因为没有这套一致性校验逻辑所以不会报错。
解决方案

按优先级从高到低可选:

  • 重构嵌套滚动视图的职责划分
    不要在tableView(_:cellForRowAt:)方法中操作cell内部collectionView的注册、代理设置、reload逻辑。将collectionView的delegate、dataSource实现下沉到自定义TestTableViewCell内部,每个cell独立管理自己内部collectionView的状态、数据、滚动偏移,从根源避免复用状态错乱。
    注意:collectionView的cell注册只需要在cell的awakeFromNib方法中执行一次,不要重复注册。同时重写cell的prepareForReuse方法,在复用前重置collectionView的偏移量、临时数据,避免上一个cell的状态残留。
  • 临时兼容修复
    如果暂时没有时间重构架构,直接在tableView初始化时关闭预取功能即可绕过校验:
    tableView.prefetchingEnabled = false
    
    该操作对常规列表的滑动流畅度影响极小,可以立刻消除断言和cell消失的问题。
  • 修正reload调用时机
    给cell增加专门的数据绑定方法,只有当传入的新数据和当前cell持有的数据不一致时,再触发collectionView的reload,避免无意义的reload打乱系统的复用缓存逻辑。
修正后代码参考
// TestTableViewCell 内部实现
class TestTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
    @IBOutlet weak var collectionView: UICollectionView!
    private var itemList: [Any] = []
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // 仅初始化时执行一次注册、代理设置
        collectionView.register(UINib(nibName: "TestCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "TestCollectionViewCell")
        collectionView.delegate = self
        collectionView.dataSource = self
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        // 复用前重置状态
        itemList.removeAll()
        collectionView.setContentOffset(.zero, animated: false)
        collectionView.reloadData()
    }
    
    // 对外仅暴露数据绑定接口
    func config(with items: [Any]) {
        itemList = items
        collectionView.reloadData()
    }
    
    // MARK: - CollectionView 代理/数据源实现放在cell内部
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return itemList.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionViewCell", for: indexPath) as! TestCollectionViewCell
        // 配置collectionCell内容
        return cell
    }
}

// 外层TableView数据源实现
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as? TestTableViewCell else {
        fatalError("Cell dequeue failed")
    }
    // 仅做数据绑定,不操作cell内部collectionView的配置
    cell.config(with: sectionData[indexPath.row])
    return cell
}

内容的提问来源于stack exchange,提问作者Samuel Hinchcliffe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 01:06:21