Search code examples
iosswiftuicollectionviewautolayoutautosize

AutoSizing cells: cell width equal to the CollectionView


I'm using AutoSizing cells with Autolayout and UICollectionView.

I can specify constraints in code on cell initialization:

  func configureCell() {
    snp.makeConstraints { (make) in
      make.width.equalToSuperview()
    }
  }

However, the app crashes as the cell hasn't been yet added to the collectionView.

Questions

  1. At which stage of the cell's lifecycle it is possible to add a constraint with cell's width?

  2. Is there any default way of making a cell'swidthequal to the widthof thecollectionViewwithout accessing an instance of UIScreenorUIWindow`?

Edit The question is not duplicate, as it is not about how to use the AutoSizing cells feature, but at which stage of the cell lifecycle to apply constraints to achieve the desired result when working with AutoLayout.


Solution

  • To implement self-sizing collection view cells you need to do two things:

    1. Specify estimatedItemSize on UICollectionViewFlowLayout
    2. Implement preferredLayoutAttributesFitting(_:) on your cell

    1. Specifying estimatedItemSize on UICollectionViewFlowLayout

    The default value of this property is CGSizeZero. Setting it to any other value causes the collection view to query each cell for its actual size using the cell’s preferredLayoutAttributesFitting(_:) method. If all of your cells are the same height, use the itemSize property, instead of this property, to specify the cell size instead.

    This is just an estimate which is used to calculate the content size of the scroll view, set it to something sensible.

    let collectionViewFlowLayout = UICollectionViewFlowLayout()
    collectionViewFlowLayout.estimatedItemSize = CGSize(width: collectionView.frame.width, height: 100)
    

    2. Implement preferredLayoutAttributesFitting(_:) on your UICollectionViewCell subclass

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
    
        // Specify you want _full width_
        let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
    
        // Calculate the size (height) using Auto Layout
        let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow)
        let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
    
        // Assign the new size to the layout attributes
        autoLayoutAttributes.frame = autoLayoutFrame
        return autoLayoutAttributes
    }