0

I have the following setup:

  • UICollectionView with self sizing cells, where their height is defined by their content

  • UICollectionViewCompositionalLayout that defines the dimensions of items like this

    let cellHeight: CGFloat = 260 
    let heightDimension = NSCollectionLayoutDimension.estimated(cellHeight)
    let fractionalWidth: CGFloat = 1.0
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(fractionalWidth),
                                         heightDimension: heightDimension)
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    let groupWidth = NSCollectionLayoutDimension.fractionalWidth(1.0)
    let groupSize = NSCollectionLayoutSize(widthDimension: groupWidth,
                                          heightDimension: heightDimension)
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                     subitems: [item])
    

This works fine, until I want to update the height of one of the cells. I get the following warning:

   [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)

"<NSAutoresizingMaskLayoutConstraint:0x6000013bdfe0 h=--& v=--& UIView:0x7fd64017a040.height == 125   (active)>",
"<NSLayoutConstraint:0x6000013b3b60 'MyCell.contentView.heightAnchor' UIView:0x7fd64017a040.height == 231   (active)>"

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000013b3b60 'MyCell.contentView.heightAnchor' UIView:0x7fd64017a040.height == 231   (active)>

The original height of the cell is indeed 125 defined by the placeholder size, however when the update comes over the network I'm creating a height constraint on the cells contentView like this:

cell.heightConstraint = cell.contentView.heightAnchor.constraint(equalToConstant: size.height)
cell.heightConstraint.identifier = "MyCell.contentView.heightAnchor"
cell.heightConstraint.isActive = true
snapshot?.reloadItems([cellIdentifier])

Where the size.height is the new height. So, the question is, what am I missing here? I'm assuming NSAutoresizingMaskLayoutConstraint is created by the Autolayout from the original height of the cell, but why is it not reset/removed when I add the height constraint to the cells contentView?

lawicko
  • 7,246
  • 3
  • 37
  • 49

2 Answers2

3

I thought some more about this and I ended up removing the height constraint all together. I figured that if I go with the self sizing cells philosophy, then I should not need any height constraints, but the height should be determined by properly defined constraints on the cell contents.

So I examined my cell once again and I found a problem that some of the content was added to the cell itself, not contentView. After fixing that and removing the height constraint, I changed this:

snapshot?.reloadItems([cellIdentifier])

into this:

self?.collectionView.collectionViewLayout.invalidateLayout()

and everything started working correctly.

lawicko
  • 7,246
  • 3
  • 37
  • 49
-1

I found it was better to design the cell in a separate xib file. In that file, set the topmost "Cell" element's Size inspector > Layout to "Inferred (Constraints)". That should remove the autoresizing mask that is conflicting with the constraint you set up.

If that is fine, then maybe the layout is creating that autoresizing mask itself, and you can invalidate the layout in order for it to recreate the autoresizing mask according to the cell's desired size.

Donovan Voss
  • 1,450
  • 13
  • 12
  • Thanks for the idea on invalidating the layout, this lead me to the proper solution to my problem. – lawicko Oct 01 '20 at 14:39