Search code examples
iosswiftuicollectionviewuicollectionviewlayout

Subclassing UICollectionViewLayout with cached UICollectionViewLayoutAttributes


I'm trying to understand how UICollectionViewLayout works and found this sample code.

https://developer.apple.com/documentation/uikit/uicollectionview/customizing_collection_view_layouts

In the sample code(and all other examples I could find), it has cachedLayoutAttributes object and calculates all items' layout in prepare().

I found 2 issues for this pattern.

One is if the number of data source is big enough, calculating everything in the prepare() method will take time(and it will be recalculated whenever collectionView's layout changes), so it doesn't look very efficient.

The other issue is that there is no chance layoutAttributesForItem or layoutAttributesForSupplementaryView get called. Only layoutAttributesForElements gets called and it seems it is sufficient enough to layout cells/supplementary views.

I read documentations but it only says the methods must be override. My best guess is that I should use override those methods to return an appropriate UICollectionViewLayoutAttributes and call them from layoutAttributesForElements. However this layoutAttributesForElements gets called whenever user scrolls UICollectionView, so it also seems not very efficient.

I want to know what is the case layoutAttributesForItem and layoutAttributesForSupplementaryView get called, and what/when/where is the best way/place/time to calculate UICollectionViewLayoutAttributes if the number of data source is huge.

Update

Thanks for the comment. However it seems like it's about optimization in invalidation cycle.

Layout objects that are designed to support invalidation contexts can use the information in a UICollectionViewLayoutInvalidationContext object to optimize their behavior during the invalidation cycle.

Which means the UICollectionViewLayoutAttributes are already set and invalidate only necessary part, right?

So, where should I calculate UICollectionViewLayoutAttributes for the first time? Please let me know if I'm wrong.


Solution

  • One is if the number of data source is big enough, calculating everything in the prepare() method will take time(and it will be recalculated whenever collectionView's layout changes), so it doesn't look very efficient.

    Yeah, it doesn't look very efficient. It's your job to make prepare() calculations as efficient as possible.

    For example: calculate only a portion of attributes and calculate more when scroll changed its position using shouldInvalidateLayout(forBoundsChange:). But be careful with this method because it might invalidate the layout on every pixel scroll.

    prepare() method is not called just for fun, it informs you that something is changed and recalculation is required! But the information about changes is hard to get. You can check how IGListKit gets information about changes before prepare: link.

    The other issue is that there is no chance layoutAttributesForItem or layoutAttributesForSupplementaryView get called. Only layoutAttributesForElements gets called and it seems it is sufficient enough to layout cells/supplementary views.

    layoutAttributesForElements(in:) is called first because the collection doesn't know about the order of your cells and supplementary views. So it asks for all attributes in the specified rectangle. Try to insert/move/delete/reload cells, I believe layoutAttributesForItem(at:) might be called. Or maybe not. But you should not worry about it because you already have cache of attributes.

    Since you cache attributes layoutAttributesForElements(in:) should be efficient enough and will look something like this:

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return attributes
            .reduce(into: [UICollectionViewLayoutAttributes](), { $0 += $1.filter({ $0.frame.intersects(rect) }) })
    }
    

    I personally suggest to look inside some difficult open-source layout. You will get much more information about the UICollectionViewLayout compared to information from any simple sample project.