3

I'm using relatively simple compositional layout, and it works, but in some cases last one or two cells in my UICollectionView seems to be with broken width.

My collectionView consists of cells with dynamic width and static height. I can calculate the height beforehand, because my cell is basically UILabel plus background UIView plus top/left/right/bottom constraints (each one with UIPriority(1000)).

topicBackView = UIView()
topicBackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(topicBackView)
        
topicLabel = MyLabel()
topicLabel.translatesAutoresizingMaskIntoConstraints = false
topicLabel.numberOfLines = 0
topicLabel.font = cs.usualFont
topicLabel.textColor = UIColor.black
topicLabel.textAlignment = .center
contentView.addSubview(topicLabel)

let tpp = topicBackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0)
tpp.priority = UILayoutPriority(1000)
tpp.isActive = true

and so on.

I know the font for UILabel, I know all the constraints' constants, so it's easy to predict the height of the cell.

Because of that, my layout looks like that:

func giveMeFreeTagsLayout() -> UICollectionViewLayout {
    let estimatedHeight: CGFloat = topicsCellCollectionHeight
    let estimatedWidth: CGFloat = 200

    let itemSize = NSCollectionLayoutSize(
        widthDimension: .estimated(estimatedWidth),
        heightDimension: .absolute(estimatedHeight)
    )
    // height is absolute because I know it, and in some cases not in this one,
    // though I have to calculate the height of UICollectionView 
    let item = NSCollectionLayoutItem(layoutSize: itemSize)

    item.edgeSpacing = NSCollectionLayoutEdgeSpacing(
        leading: .fixed(0), 
        top: .fixed(8), 
        trailing: .fixed(8), 
        bottom: .fixed(8)
    )

    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(estimatedHeight)
    )
    let group = NSCollectionLayoutGroup.horizontal(
        layoutSize: groupSize,
        subitems: [item]
    )
    group.contentInsets = NSDirectionalEdgeInsets(
        top: 0, 
        leading: 16, 
        bottom: 0, 
        trailing: 16
    )

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

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

The thing is, it works in simulator on iPhone 11, but this collectionView seems to be broken when I'm trying to emulate this on smaller screens (in my case, iPad Air 3rd generation with iPhone compatibility).

In this case one or two last cells have broken width, and I don't know why:

Bad one

How it should be: Good one (works like a charm)

What is happening here?

Xinqi Mu
  • 3
  • 1
  • 3
lithium
  • 1,272
  • 1
  • 14
  • 28

3 Answers3

3

let estimatedWidth: CGFloat = 200 change this to 50 and it will work

devarshi
  • 71
  • 8
3

I use cell.sizeToFit() - when create a cell

and this code then create a compositional layout:

    let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(1), heightDimension: .estimated(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    let groupSize = NSCollectionLayoutSize(widthDimension: .estimated(1), heightDimension: .estimated(1))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
Vladimir Moor
  • 81
  • 1
  • 5
0

OPs code works well with the estimatedWidth suggestion in the accepted answer.

Using item.edgeSpacing might leave extra space at the trailing edge and bottom (I haven't verified it).

The following way to space the items won't have that issue:

group.interItemSpacing = .fixed(8)

let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 8
Andrew Stoddart
  • 408
  • 4
  • 9