iosuicollectionviewuicollectionviewcompositionallayout

Multiple column layout with collection view compositional layout


I am converting some hairy collection view code (scroll views with embedded stack views, collection views, table views, etc.) to a single collection view with compositional layout. But I am having troubles with “two column” layout when using the count parameter.

To experiment, I downloaded Apple’s Implementing Modern Collection Views sample, looked at the Create a Column Layout code, which has this example:

extension TwoColumnViewController {
    /// - Tag: TwoColumn
    func createLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .absolute(44))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
        let spacing = CGFloat(10)
        group.interItemSpacing = .fixed(spacing)

        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = spacing
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }
}

In their demo app, you get there by navigating to “Compositional Layout” » “Getting Started” » “Two-Column Grid”. Anyway, this yields:

enter image description here

OK, that’s fine.

But in iOS 16, horizontal(layoutSize:subitem:count:) is deprecated:

enter image description here

And if you replace it with horizontal(layoutSize:repeatingSubitem:count:), you get a layout like the following (where you cannot even scroll to the right to see the cells offscreen):

enter image description here

How do you get columnar output in iOS 16 and later?


Solution

  • Unlike horizontal(layoutSize:subitem:count:), with horizontal(layoutSize:repeatingSubitem:count:) you have to adjust the width of the item or group. E.g.:

    extension TwoColumnViewController {
        /// - Tag: TwoColumn
        func createLayout() -> UICollectionViewLayout {
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),  // NB: 0.5, not 1.0
                                                  heightDimension: .fractionalHeight(1))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                   heightDimension: .absolute(44))
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: 2)
            let spacing = CGFloat(10)
            group.interItemSpacing = .fixed(spacing)
    
            let section = NSCollectionLayoutSection(group: group)
            section.interGroupSpacing = spacing
            section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
    
            let layout = UICollectionViewCompositionalLayout(section: section)
            return layout
        }
    }
    

    As the documentation for the new method says:

    It’s your responsibility to ensure that the group’s layoutSize can fit count repetitions of this item.

    I find that I can either adjust the widthDimension of the NSCollectionLayoutItem of the item or the group to .fractionalWidth(0.5), and it renders the two-column collection view.

    I hope the above helps someone else save a little time when working through Apple’s compositional layout examples.