Ios – Container view with dynamic height inside UIScrollView with auto layout

autolayoutios

I have a UIScrollView, nested inside of which in one content view and it has two child views nested, a regular UIView with a known height, and a container view with dynamic height depending on is content. Like so:

Main View hierarchy

The view looks like this:

Main View

My constraints are setup as follows:

The Scroll View is constrained to the trailing, leading, top and bottom edges of its superview (i.e. the View)

The Content View is constrained to the trailing, leading, top and bottom edges of its superview (i.e. the Scroll View)
It also has a width constraint equal to the main view (i.e. the View) so the width of the scrollview is the same as the screen width.

The top view is constrained to the leading, trailing and top edges of its superview (i.e. the Content View)

The Container View is constrained to the trailing, leading and bottom edges of its superview (the Content View)
Its top edge is also constrained to the bottom edge of the Top View.

The view hierarchy of the Container View looks like this:

Content View heirarchy

Content View

The top left label is constrained to the trailing, leading and top edges of its superview.
The bottom right label is constrained to the trailing, leading and bottom edges of its superview.
The top label has a vertical constraint to the bottom label. I've made this vertical constraint extra large for the purpose of my tests (1000 points).

This should give the content view a height of ~1000 points.

My understanding was with a height now resolved for the container view, that the Content View would resize to the height of the Top View + the height of the Container View.

But IB complains with the following:

IB

It wants to resize the Container View and give it a height of 0. If I give the Container View an explicit height then everything works as expected but this is not what I need want since the Container View can dynamically change depending on its content size.

Any help is appreciated!

Best Answer

There were two things I found that helped me work around this same issue.

Issue 1: Height of Container

I want the container view to resize itself, but IB wants an explicit height on the container view. Since IB doesn't know anything about the content of the view, it has no way of knowing the content of the container view can size itself. The simplest way to do that is to set a Placeholder Intrinsic Content Size from the Size Inspector for the container view:

Size Inspector

This effectively results in making IB happy without applying any height constraint. Another option is to add a "Remove at build time" height constraint on the container.

Issue 2: Height of Child View Controller View

The root view of the child view controller defaults to using an AutoresizingMask, as is standard for the topmost view in any UIViewController. My solution to this is to disable use of AutoresizingMask in prepareForSegue, when the child view controller is added. Try the following in the parent view controller:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    if let childViewController = segue.destination as? ChildViewControllerClass {
        childViewController.view.translatesAutoresizingMaskIntoConstraints = false
    }
}

Making the change in the parent view ensures that if the child view is re-used elsewhere inside a UINavigationController, the view will be sized correctly.

Before I made that change, I kept getting AutoLayout errors conflicting with constraints called UIView-Encapsulated-Layout-Height, which I believe is the name for constraints derived from AutoresizingMask based layout on the root UIViewController view.