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

iOS14调用setCollectionViewLayout崩溃,iOS16正常求助

iOS14.1切换UICollectionView布局崩溃的解决方案

问题分析

崩溃日志显示调用-[__NSDictionaryM setObject:forKey:]时传入了nil对象,key对应cell的indexPath。结合系统版本差异和代码逻辑,核心原因是iOS14的UICollectionView布局系统在处理复用cell indexPath的装饰视图时存在兼容性bug:

  • 代码中为分隔符装饰视图使用了与对应cell完全相同的indexPath
  • iOS16对布局属性的处理逻辑做了优化,规避了该冲突,但iOS14中系统尝试将装饰视图属性存入内部字典时会出现nil对象
  • 另一个ViewController在iOS14环境下正常,大概率是布局切换时的数据源状态、布局类型差异刚好避开了这个bug

具体修复方案

1. 为装饰视图生成独立的IndexPath

避免与cell的indexPath重复,修改layoutAttributesForElements方法中创建装饰视图属性的代码:

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes] {
    let layoutAttributes = super.layoutAttributesForElements(in: rect) ?? []
    let lineWidth: CGFloat = Const.minimumLineSpacing
    var decorationAttributes: [UICollectionViewLayoutAttributes] = []

    // 跳过第一个cell
    for layoutAttribute in layoutAttributes where layoutAttribute.indexPath.item > 0 {
        // 生成独立的indexPath,避免与cell的indexPath冲突
        let decorationIndexPath = IndexPath(item: layoutAttribute.indexPath.item + Int.max / 2, section: layoutAttribute.indexPath.section)
        let separatorAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: "separator", with: decorationIndexPath)
        let cellFrame = layoutAttribute.frame
        separatorAttribute.frame = CGRect(x: cellFrame.origin.x, y: cellFrame.origin.y - lineWidth, width: cellFrame.size.width, height: lineWidth)
        separatorAttribute.zIndex = Int.max
        decorationAttributes.append(separatorAttribute)
    }

    return layoutAttributes + decorationAttributes
}

2. 调整prepare方法的调用顺序

确保先执行父类的prepare方法初始化布局基础参数,再设置自定义的itemSize:

override func prepare() {
    super.prepare() // 先调用父类方法完成基础初始化
    guard let collectionView = collectionView, collectionView.numberOfSections > 0 else { return }

    itemSize = CGSize(width: collectionView.frame.width - Const.leftRightSpacing * 2, height: itemHeight)
}

3. 切换布局时的额外处理(可选)

如果上述修复仍有问题,在切换布局前先让旧布局失效,确保系统清理旧的布局属性:

collectionView.collectionViewLayout.invalidateLayout()
collectionView.setCollectionViewLayout(mode.layout, animated: false)

验证

完成修改后,在iOS14.1环境下测试布局切换,确认崩溃不再出现;同时验证iOS16环境下功能保持正常。

内容的提问来源于stack exchange,提问作者Omeir Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 06:55:38