iOS15系统下UICollectionViewFlowLayout应用NSDiffableDataSource快照时崩溃如何解决?
iOS 15 对 UICollectionViewFlowLayout 的 invalidateLayoutWithContext: 方法新增了严格的入参类型校验,要求传入的上下文必须是 UICollectionViewFlowLayoutInvalidationContext 类或其子类实例,不允许传入 nil 或父类 UICollectionViewLayoutInvalidationContext 的实例。而 iOS 14 及更低版本没有该校验逻辑,因此不会触发崩溃。
该问题出现在你调用 apply(_:animatingDifferences:) 且 animatingDifferences 设为 true 时,iOS 15 系统的 NSDiffableDataSource 内部首次触发布局刷新时,错误传递了不符合要求的无效上下文给布局对象,直接触发了系统校验的异常。
方案1:首次加载Snapshot时关闭动画
首次加载列表不需要做差异动画,直接把animatingDifferences设为false即可,后续增量更新数据时再开启动画不影响使用,是成本最低的修复方案:// 首次加载时关闭动画 dataSource.apply(snapshot, animatingDifferences: false)方案2:自定义
UICollectionViewFlowLayout子类兼容异常参数
如果需要保留首次加载的动画效果,可以重写布局的无效化方法,对入参做类型兼容处理:class CompatibleFlowLayout: UICollectionViewFlowLayout { override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) { // 校验上下文类型,不符合要求时创建正确的实例 guard let flowContext = context as? UICollectionViewFlowLayoutInvalidationContext else { let validContext = UICollectionViewFlowLayoutInvalidationContext() // 复制原上下文的配置参数 validContext.invalidateEverything = context.invalidateEverything validContext.invalidateDataSourceCounts = context.invalidateDataSourceCounts super.invalidateLayout(with: validContext) return } super.invalidateLayout(with: flowContext) } }把你的CollectionView所用的布局替换为上述自定义布局即可修复问题。
方案3:关闭自动估算尺寸
如果你的列表项使用固定尺寸,可移除estimatedItemSize相关的自动估算配置,指定固定的itemSize,也可以规避该系统异常。
内容的提问来源于stack exchange,提问作者Marcel

