9

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
  • 179
  • 7
  • 1
    Same as https://stackoverflow.com/questions/70121104/ios-15-uicollectionview-issue-for-uicollectionviewrecursion – matt Dec 03 '21 at 12:02
  • But there is no solution in the above link as well @matt – Deepa Bhat Dec 04 '21 at 13:32
  • Very true, I didn't say there was. I said they were the same question, which they very obviously are. Let's try to keep things consolidated. There's no point asking the same question twice. You can watch the other question, or even add a bounty to it, but merely re-asking does nothing for your chances of getting an answer, and it makes Stack Overflow messy. – matt Dec 04 '21 at 13:59
  • Now, if you had added sufficient code to reproduce the issue, that would be another matter. But you didn't. 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
  • 5
    Has anyone found more information about this? Im currently experiencing the same problem with iOS 15.2. I've been trying to fix this issue for a couple of days now without any luck so far... – Vinchenzo Jan 05 '22 at 23:03
  • After weeks of trying different things, I fixed my own issue by removing the sizeForItamAt method. – Vinchenzo Jan 22 '22 at 20:24

3 Answers3

2

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,271
  • 3
  • 21
  • 43
  • 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
2

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.

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