10

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.'

Deepa Bhat
  • 174
  • 1
  • 10
  • 1
    Same as https://stackoverflow.com/questions/70121104/ios-15-uicollectionview-issue-for-uicollectionviewrecursion – matt Dec 03 '21 at 12:02
  • 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. – matt Dec 04 '21 at 14:01
  • @matt 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! – Deepa Bhat Dec 05 '21 at 11:18
  • Well I've explained many times how I deal with making cells self sizing, e.g. https://stackoverflow.com/a/51585910/341994 – matt Dec 05 '21 at 14:19
  • After weeks of trying different things, I fixed my own issue by removing the sizeForItamAt method. – Vinchenzo Jan 22 '22 at 20:24

4 Answers4

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.

air7y
  • 51
  • 1
  • 3
  • 1
    Works for me. Alternatively, estimated size may be set/changed in code: `collectionView.collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize` – brigadir Apr 24 '22 at 04:18
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 :)

Zaporozhchenko Oleksandr
  • 4,660
  • 3
  • 26
  • 48
  • Really hope it can help you or lead to your own solution. – Zaporozhchenko Oleksandr Feb 05 '22 at 08:42
  • Same as your https://stackoverflow.com/questions/70121104/ios-15-uicollectionview-issue-for-uicollectionviewrecursion/70996410#70996410. Don't do that, please. Delete the other one; feel free to put a _comment_ pointing to this one! – matt May 18 '22 at 15:00
0

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
}
Julia K.
  • 113
  • 1
  • 6
-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

Abhishek B
  • 157
  • 8