Self sizing collection view enters a recursive loop in iOS 15
Asked Answered
M

4

12

I have a self sizing collection view and when I call super.layoutSubviews my app crashes since the collection view enters a recursive update loop. This was working fine in iOS 14 and below. But observed it in iOS 15 onwards.

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()
}

}

Crash says:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView (<KPFlagship.SelfSizingCollectionView 0x7f896b260e00>) is stuck in its update/layout loop. This can happen for many reasons, including self-sizing views whose preferred attributes are not returning a consistent size. To debug this issue, check the Console app for logs in the "UICollectionViewRecursion" category.'

Metabolic answered 3/12, 2021 at 11:12 Comment(5)
Same as #70121604Yearly
It seems to me obvious you're doing something very bizarre in your layoutSubviews but I can't say more without code. There is a right way and a wrong way to get cells to self-size in a collection view, and this looks like the wrong way. But again, you didn't ask how to do it; you just complained that your way stopped working.Yearly
@Yearly I have edited the code and yes it started crashing after updation to iOS 15. So what change do you suggest to make it work? Plus the UI should also support dynamic font for the subviews in collection view cells!Metabolic
Well I've explained many times how I deal with making cells self sizing, e.g. https://mcmap.net/q/122189/-uicollectionviewcell-dynamic-height-w-two-dynamic-labels-amp-auto-layoutYearly
After weeks of trying different things, I fixed my own issue by removing the sizeForItamAt method.Allograph
D
5

Was facing the same crash on iOS15 when manually calculating cell size.

By disabling Estimated Size on collectionView in IB by setting it to None was fix for me.

Dietitian answered 23/2, 2022 at 14:0 Comment(1)
Works for me. Alternatively, estimated size may be set/changed in code: collectionView.collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSizeObsequious
U
3

In our case (vertically flow layout collectioview, vertically self-sizing cells), problem was that I had some cells that didn't implement overriding 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
}

here is flowlayout similar to what we had:

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
    }
}

So, In ios versions before ios 15 collectionview and cells had no problem with it. After building the same code to ios 15+ started to stuck in its update/layout loop.

So, if you have a similar problem, try to figure out which type of custom layout you are using and try return according preferredLayoutAttributesFitting.

P.S. If you (person reading that) have any insights why it worked before and do not work after ios15, what actually has changed in ios15 that leads to such problem, please share with us, ty :)

Unifoliolate answered 5/2, 2022 at 8:42 Comment(2)
Really hope it can help you or lead to your own solution.Unifoliolate
Same as your #70121604. Don't do that, please. Delete the other one; feel free to put a comment pointing to this one!Yearly
L
1

I had similar problem in iOS 15. Fixed it by overriding UICollectionViewFlowLayout method:

override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, 
         withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool {
    return true
}
Lovash answered 18/5, 2022 at 14:56 Comment(0)
F
-1

I was facing same issue, because after iOS 15 there is issue for self-sizing cells. can find the discussion over apple developer forum : https://developer.apple.com/forums/thread/694141.

To fix this I have changed the estimated size to none in IB size inspector as shown in below image. Estimated size in Size Inspector

Fit answered 20/3, 2023 at 11:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.