TemptingFriendlyGrison TemptingFriendlyGrison - 5 months ago 80
iOS Question

Sizing a Container View with a controller of dynamic size inside a scrollview

I'm trying to create a container view, with a controller that has a dynamic height, inside a UIScrollView and have it sized automatically using auto layout.

Storyboard illustrating the setup

View Controller A is the scrollview, which has the container view included, along with more content below.

View Controller B is the view controller that I want to have a dynamic size and for all the content to be displayed in full height in View Controller A's Scroll View.

I'm having some problems getting the dynamic size of B to automatically set the size of the Container View in A. However if I set a height constraint on the Container View in A to for example 250, it would be the expected output if View Controller B would also have 250 height. It also works fine for height 1000, so as far as I know, all the auto layout constraints are properly setup. Unfortunately, since the height should actually be dynamic, I would like to avoid setting a height constraint at all.

I'm not sure if there are any settings for view controller B I can set for it to automatically update its size depending on its contents, or if there are any other tricks I've missed. Any help would be much appreciated!

Is there any way to size the Container View in A according to how big the size of View Controller B is without setting a height constraint?

Answer

Yup, there is. I managed to achieve that kind of behavior in one of my own projects.

All you gotta do is to tell the system that it should not add constraints that mimic the fixed frame set for your root view in Interface Builder. The best place to do this is in your container view controller when your embed segue is triggered:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // You might want to check if this is your embed segue here
    // in case there are other segues triggered from this view controller. 
    segue.destinationViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
}

Important:

You gotta make sure that the view that you load into the container is constrained from top to bottom and you need to set the priority of one of the vertical constraints to a value lower than 1000. (It's a good practice to always use the bottom constraint for this.) This is necessary because otherwise Interface Builder will complain — with a good reason:

At design time your root view has a fixed size (height). Now if all your subviews have a fixed height and are connected with fixed constraints that all have the same priority it's impossible to fulfil all these requirements unless the height of your fixed root view coincidentally matches exactly the total height of your subviews and the vertical constraints. If you lower the priority of one of the constraints to 999 Interface Builder knows which constraint to break. At runtime however — when the translatesAutoresizingMaskIntoConstraints property is set as stated above — there is no fixed frame for your root view anymore and the system will use your 999 priority constraint instead.

Interface Builder Screenshot