自调整大小的集合视图在 iOS 15 中进入递归循环

Dee*_*hat 11 uikit ios swift5 ios15

我有一个自动调整大小的集合视图,当我调用 super.layoutSubviews 时,我的应用程序崩溃,因为集合视图进入递归更新循环。这在 iOS 14 及更低版本中运行良好。但从 iOS 15 开始就观察到了这一点。

class DynamicCollectionView: UICollectionView {

override var contentSize: CGSize {
    didSet {
        invalidateIntrinsicContentSize()
    }
}

override func layoutSubviews() {
    super.layoutSubviews()
    if bounds.size != intrinsicContentSize {
        invalidateIntrinsicContentSize()
    }
}

override var intrinsicContentSize: CGSize {
    return contentSize
}

override func reloadData() {
    super.reloadData()
    invalidateIntrinsicContentSize()
    layoutIfNeeded()
}
Run Code Online (Sandbox Code Playgroud)

}

崩溃 说:

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“UICollectionView(<KPFlagship.SelfSizingCollectionView 0x7f896b260e00>)卡在其更新/布局循环中。发生这种情况的原因有很多,包括自调整大小的视图,其首选属性未返回一致的大小。要调试此问题,请检查控制台应用程序中“UICollectionViewRecursion”类别中的日志。

小智 5

在 iOS15 上手动计算单元格大小时遇到​​同样的崩溃。

通过将Estimated SizeIB 中的 collectionView 设置为禁用来None对我来说是修复。


Zap*_*ndr 3

在我们的例子中(垂直流动布局collectioview,垂直自调整大小的单元格),问题是我有一些单元格没有实现重写preferredLayoutAttributesFitting:

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
    layoutAttributes.frame.size = contentView
        .systemLayoutSizeFitting(targetSize,
                                 withHorizontalFittingPriority: .required,
                                 verticalFittingPriority: .fittingSizeLevel)
    return layoutAttributes
}
Run Code Online (Sandbox Code Playgroud)

这是类似于我们的流程布局:

final class VerticalFlowLayout: UICollectionViewFlowLayout {
    
    override init() {
        super.init()
        scrollDirection = .vertical
        estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }
        
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributesObjects = super.layoutAttributesForElements(in: rect)
        
        
        layoutAttributesObjects?.enumerated()
            .forEach { (index, layoutAttributes) in
                if layoutAttributes.representedElementCategory == .cell {
                    
                    guard let newFrame = layoutAttributesForItem(at: layoutAttributes.indexPath)?.frame else {
                        return
                    }
                    layoutAttributes.frame = newFrame
                }
            }
        
        return layoutAttributesObjects
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let collectionView = collectionView else {
            return nil
        }
        
        guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else {
            return nil
        }
        
        layoutAttributes.frame.origin.x = sectionInset.left
        layoutAttributes.frame.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right
        
        return layoutAttributes
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在ios 15之前的 ios 版本中,collectionview 和 cells 没有问题。在为ios 15+构建相同的代码后,开始陷入更新/布局循环。

因此,如果您遇到类似的问题,请尝试找出您正在使用哪种类型的自定义布局,并尝试根据preferredLayoutAttributesFitting返回。

PS如果你(读这篇文章的人)有任何见解为什么它在ios15之前有效而在ios15之后不起作用,ios15中实际上发生了什么变化导致了这样的问题,请与我们分享,ty :)