NSStackView giving too much space to collection view despite constraints

67 views Asked by At

I have an NSStackView containing an NSImageView and an NSCollectionView. Both of these items are wrapped in their own NSScrollView:

IB document outline of view hierarchy

Desired behavior

I want the collection view to take up only as much vertical space as its items require. If it has no items, its height should be 24px. If its items require multiple lines, it should show up to 2.5 without a scroll bar, and then scroll.

I want the image view to take up the rest of the space in the window (save for the "add" and "remove" buttons). The image view will always have some content. If the size of the image is smaller than the available space, I just want the image to display initially at its intrinsic size in the center of the scroll view. I've set the scroll view to allow magnification, so the user can resize the image if desired.

The window will never be vertically smaller than ~300px, so both views should always be visible.

Observed behavior

Everything is fine in the horizontal direction, it's only the vertical direction that is causing trouble.

The problem is that the collection view is almost always at its maximum height, even when it only has 0 or 1 lines of items.

example of incorrect layout

AutoLayout config

The stack view's hugging priority is the default 250 for both. The image view and its scroll view have 249 for both. I've tried setting this to 1, but it doesn't change anything.

The collection view's scroll view has 250 for the hugging priority in both directions. This is the default, but even setting it to 1000 doesn't change the behavior.

I was originally seeing the image view never grow beyond its intrinsic content height, and give all the remaining space to the collection view. But then I added a "height <= 60" constraint to the collection view's scroll view. Now things are better, but the collection view is still not shrinking to a single line when it doesn't need more vertical space. Really, I think the <= constraint is either the wrong solution or only a partial one.

You can find all the code for this at this GitHub repo. Mostly, it's IB configuration. The only code I've added to the template project is to provide items to the collection view.

1

There are 1 answers

0
DonMag On

One option worth a try...

Add an @IBOutlet connection for your scroll view:

@IBOutlet weak var scrollView: NSScrollView!

then, in viewDidLoad():

override func viewDidLoad() {
    super.viewDidLoad()

    let c = scrollView.heightAnchor.constraint(equalTo: collectionView.heightAnchor, multiplier: 1.0)
    c.priority = .required - 1
    c.isActive = true
}

As you resize the window and add/remove items, the scroll view Height will auto-adjust.

However... on launch the scroll view Height won't be correct.

I work with iOS, and I'm not familiar with the view/controller lifecycle, so at some point you'll need to force another layout pass.

Alternatively... you might want to look at NOT using a collection view. Here is one approach (iOS, but shouldn't be difficult to convert to MacOS): https://stackoverflow.com/a/60588546/6257435