Search code examples
swiftcocoa-touchuicollectionviewuicollectionviewlayoutuicollectionviewcompositionallayout

UICollectionViewCompositionalLayout horizontal, not calculating width of content when using estimated and not horizontally hugging


I want the cell to horizontaly Hug its content

I am using UICollectionViewCompositionalLayout in horizontal mode and with widthDimension: .estimated(100) which should calculate the content size

but the width of cell / group is NOT calculated by the content size at all and is set to the estimated values as it would be an absolute value

Even adding a width constraint to the label doesn't affect the cells size

Is this expected behavior? When yes, then why? and how to get the desired result?

enter image description here

enter image description here

class LabelCell: UICollectionViewCell {
    @IBOutlet weak var label: UILabel!
}

class ViewController: UIViewController, UICollectionViewDataSource {

    func buildLayout() -> UICollectionViewCompositionalLayout {

        let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(100), heightDimension: .absolute(32))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .estimated(100), heightDimension: .absolute(32))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)

        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = 10

        let configuration = UICollectionViewCompositionalLayoutConfiguration()
        configuration.scrollDirection = .horizontal
        return UICollectionViewCompositionalLayout(section: section, configuration: configuration)

    }

    @IBOutlet weak var collectionView: UICollectionView!

    let items = ["1", "12", "12345", "123456789"]

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.collectionViewLayout = buildLayout()
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: "LabelCell", for: indexPath) as! LabelCell
        c.label.text = items[indexPath.row]
        return c
    }
}

Solution

  • To size your cells correctly, change:

    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)

    To this:

    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

    I'm unsure why this change is necessary, as these lines appear to say the same thing. That said, it will work provided your collection view cell specifies the actual cell size by overriding sizeThatFits(_:), preferredLayoutAttributesFitting(_:), using Auto Layout, etc.

    For what it's worth, the documentation for the first function signature says:

    Specifies a group that will have N items equally sized along the horizontal axis.

    The comment for the second function signature makes no such claims about equally sized items. It says:

    Specifies a group that will repeat items until available horizontal space is exhausted.