rlr
rlr

Reputation: 330

Making the width of a group in UICollectionViewCompositionalLayout equal to collectionView's width

I am using the UICollectionViewCompositionalLayout to layout my cells in a collectionView. I want the width of the items in a row to auto adjust so as to fill the collectionView's width. However this is not happening even after setting the fractionalWidth to 1.0 on groupSize. Here is the code:

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView = UICollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout())
    collectionView.backgroundColor = UIColor(red: 157.0/255.0, green: 159.0/255.0, blue: 162.0/255.0, alpha: 0.5)
    collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    collectionView.dataSource = dataSource
}

private func createCollectionViewLayout() -> UICollectionViewLayout {
    // Define Item Size
    let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(100.0), heightDimension: .absolute(52.0))
    
    // Create Item
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    // Define Group Size
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(52.0))
    
    // Create Group
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [ item ])
    group.interItemSpacing = .fixed(1.0)
    
    // Create Section
    let section = NSCollectionLayoutSection(group: group)
    
    return UICollectionViewCompositionalLayout(section: section)
}

enter image description here

Upvotes: 3

Views: 1371

Answers (1)

DonMag
DonMag

Reputation: 77672

You're running into a couple problems.

A UICollectionView is designed to take the cell widths you have declared - explicitly or with or auto-layout determined sizes - and arranges them to "wrap" or scroll.

You're trying to get the collection view to set the sizes for you.

The most straightforward way to get the layout you want is for you (the designer/developer) to decide how wide you want your cells as percentages of the width.

So, you may want:

20% |    40%    | 20% | 20%

but you also want 1-point inter-item spacing.

Because collection views won't layout cells on "partial pixels," you need to allow for approximately 1-point inter-item spacing.

To get that, make sure you percentages add up to less-than 100%:

20% |    39%    | 20% | 20%

and use:

group.interItemSpacing = .flexible(1.0)

Here's a modified version of your createCollectionViewLayout() func:

private func createCollectionViewLayout() -> UICollectionViewLayout {

    // array of cell widths as Percentages
    //  total must be less than 1.0 to leave space
    //  for inter-item spacing
    let percentageWidths: [CGFloat] = [
        0.20, 0.39, 0.20, 0.20, // total == 0.99
    ]

    // array of NSCollectionLayoutItem
    var items: [NSCollectionLayoutItem] = []
    percentageWidths.forEach { w in
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(w), heightDimension: .absolute(52.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        items.append(item)
    }

    // Define Group Size
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(52.0))
    
    // Create Group with items array
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: items)

    // use .flexible(1.0) for .interItemSpacing
    //  so we get *about* 1.0-points between cells
    group.interItemSpacing = .flexible(1.0)

    // Create Section
    let section = NSCollectionLayoutSection(group: group)
    
    return UICollectionViewCompositionalLayout(section: section)
    
}

Since you said: "proportional" seems to be the thing I am looking for ... that might be sufficient for you.

If you really want exactly 1-point-wide inter-item spacing, you'll have some more work to do... you'll need to calculate your actual cell widths and convert them to percentages of the total, allowing for an extra 3-points for the spacing, and then round the values so they don't fall on partial-pixels.

Upvotes: 2

Related Questions