0

The goal is to configure a UICollectionViewCompositionalLayout for full-width cells which contain a fixed-aspect ratio UIImageView (plus some other views underneath including variable height UILabels and UIStackViews) and have the cells properly auto-size. My naive attempt doesn't work and I can't figure out why. The simple case I present here gives me a bunch of constraint errors at runtime.

I cribbed the code from this answer, but it looks pretty straightforward:

    override func viewDidLoad() {
        super.viewDidLoad()

        let size = NSCollectionLayoutSize(
            widthDimension: NSCollectionLayoutDimension.fractionalWidth(1),
            heightDimension: NSCollectionLayoutDimension.estimated(100)
        )
        let item = NSCollectionLayoutItem(layoutSize: size)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitem: item, count: 1)

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

        collectionView.collectionViewLayout = layout
    }

And the constraints look like this: enter image description here

And as soon as I scroll, I get these constraint errors:

2021-01-21 20:29:18.311491+0100 CompositionalLayoutDynamicHeight[96399:2597528] [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. 
(
    "<NSLayoutConstraint:0x6000020a82d0 UIImageView:0x138818600.width == 1.33333*UIImageView:0x138818600.height   (active)>",
    "<NSLayoutConstraint:0x6000020a8460 V:|-(0)-[UIImageView:0x138818600]   (active, names: '|':UIView:0x13881c4d0 )>",
    "<NSLayoutConstraint:0x6000020a84b0 H:[UIImageView:0x138818600]-(0)-|   (active, names: '|':UIView:0x13881c4d0 )>",
    "<NSLayoutConstraint:0x6000020a8500 V:[UIImageView:0x138818600]-(0)-|   (active, names: '|':UIView:0x13881c4d0 )>",
    "<NSLayoutConstraint:0x6000020a8550 H:|-(0)-[UIImageView:0x138818600]   (active, names: '|':UIView:0x13881c4d0 )>",
    "<NSLayoutConstraint:0x6000020a85a0 'UIIBSystemGenerated' CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280.leading == UIView:0x13881c4d0.leading   (active)>",
    "<NSLayoutConstraint:0x6000020a85f0 'UIIBSystemGenerated' H:[UIView:0x13881c4d0]-(0)-|   (active, names: '|':CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280 )>",
    "<NSLayoutConstraint:0x6000020a8640 'UIIBSystemGenerated' CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280.top == UIView:0x13881c4d0.top   (active)>",
    "<NSLayoutConstraint:0x6000020a8690 'UIIBSystemGenerated' V:[UIView:0x13881c4d0]-(0)-|   (active, names: '|':CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280 )>",
    "<NSLayoutConstraint:0x6000020a60d0 'UIView-Encapsulated-Layout-Height' CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280.height == 292.667   (active)>",
    "<NSLayoutConstraint:0x6000020a6120 'UIView-Encapsulated-Layout-Width' CompositionalLayoutDynamicHeight.CollectionViewCell:0x13881c280.width == 390   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000020a82d0 UIImageView:0x138818600.width == 1.33333*UIImageView:0x138818600.height   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

It seems that AutoLayout is creating that UIView-Encapsulated-Layout-Height constraint which is conflicting with the aspect ratio constraint—but why isn't it just resizing to fit it?

Robert Atkins
  • 20,676
  • 14
  • 60
  • 92
  • We've just had another very similar situation and again, the fix was to change the height estimate in `NSCollectionLayoutSize(... heightDimension:)` to an _over_ estimate. If it's an underestimate, we get constraint violations at runtime. If it's an overestimate, everything is fine. – Robert Atkins Feb 16 '21 at 10:53

1 Answers1

1

Auto-layout makes multiple "passes" when laying out the UI elements for Collection views (and Table views).

Frequently, particularly when using variable-sized cell content, auto-layout will throw warnings as it walks its way through the constraints.

Probably the easiest way to get rid of that warning is to give your Aspect Ratio constraint a Priority of less-than Required.

If you set it to Default High (750), that tells auto-layout it's allowed to "break" it without throwing the warning (but it will still be applied).

DonMag
  • 44,662
  • 5
  • 32
  • 56
  • This did indeed work for my reduced case above, but the real layout was a lot trickier and involved me having to make the height constraint of a bunch of views "required" and set appropriate content hugging and compression resistance in a bunch of places—it seems autolayout will do anything it can to satisfy the constraints *except* shrink or expand the cell, unless it's forced to by `required`s. – Robert Atkins Jan 26 '21 at 08:43
  • hmmm... in general, that shouldn't be needed. If you want to share your actual layout, maybe I can give you some tips to make things easier. – DonMag Jan 26 '21 at 14:56