UICollectionViewFlowLayout with multiple columns and dynamic cell sizing

Viewed 58

My goal is to implement a collection that looks like so: Instagram's discover tab

I can achieve a similar result using UICollectionViewCompositionalLayout but I am constrained to iOS versions lower than 13.0, which makes this a no go.

I have been testing UICollectionViewFlowLayout and I can achieve multiple columns using collectionView(_:layout:sizeForItemAt:), but I can't find a way to dynamically calculate the height of the cells, which can be either an image, a label, etc.

Thanks

1 Answers

You need to subclass UICollectionViewFlowLayout which will provide you an opportunity to all the modifications to cell frames and more.

import UIKit

class InstagramCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        
        // These attributes have all the information about how 
        // collectionView's default layout will layout your cells
        guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
        
        guard let collectionView = self.collectionView else { return nil }
        
        var newAttributes: [UICollectionViewLayoutAttributes] = []
        
        let layout = collectionView.collectionViewLayout
        
        for attribute in attributes {
            if let cellAttribute = layout.layoutAttributesForItem(at: attribute.indexPath) {
                // Decide which indexPaths' cells should appear bigger
                if attribute.indexPath.item % 2 == 0 {
                    // whatever modifications you want for big cells
                    cellAttribute.frame.size = CGSize(width: 200, height: 200)
                }
                else {
                    // whatever modifications you want for small cells
                    cellAttribute.frame.size = CGSize(width: 70, height: 70)
                }
                
                // Since you are modifying default layout and making some cells bigger
                // You will need to accoun for extra height in your calculations
                // I am not doing that here

                newAttributes.append(cellAttribute)
            }
        }
        
        return newAttributes
    }
}

Usage

let igFlowLayout = InstagramCollectionViewFlowLayout()
collectionView.collectionViewLayout = igFlowLayout

Notes

This is just an idea of how you are supposed to create a subclass to customize cell frames. You will need to add a lot more to this according to your own implementation.

Subclassing UICollectionViewLayout also means that now you are fully responsible for telling collectionView how big it's collectionViewContentSize should be for all your data.

Reference

Customizing Collection View Layouts has all the information you need to get started on this. They explain how to achieve a similar layout in their example. enter image description here

Related