Like many iOS developers I did my sharing of trying out AutoLayout after it was introduced last year. And like many of these developers I ran into a fair share of trouble. It was not until Cessare Rochi clarified things in his session ‘AutoLayout, oh boy’ at mdevcon that I finally started finding my way around AutoLayout a bit.
First of, the consensus seems to be: do not use Interface Builder! IB gives you all kinds of challenges surrounding AutoLayout. Everyone has their own reasons for this but in short, when you use AutoLayout in code/manually, you are actually in control. So then how do we do this?
Apple has given us a nice, albeit somewhat lenghty way of setting these constraints:
[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:tab attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]
Using this to place a button on a UIView the could would look like this:
The first thing to notice is the fact that the second constraint could be formulated differently while getting the same result. In the code example I define the horizontal position of the button to lead the left edge of the superview by 50. However, if I changed the first attibute to NSLayoutAttributeLeft it would instead say that the spacing between the left side of the button and the left side of the superview should be 50. This works because it is inside of the superview. However, if you would want to space two buttons, the second option would not work, this would result in a partial overlap of the buttons since the second button would be placed 50 points from the left of the first button. Instead you would have to use the NSLayoutAttributeRight to define the constraint. If you are planning to use AutoLayout in your applications, make sure you understand the different NSLayoutAttributes and how they affect your components.
Another key element is the following line, if you do not set the TranslatesAutoresizingMaskIntoConstraints to NO/FALSE it will generate it’s own constraints, which will then conflict with yours. So you want to make sure to turn this of for all your components that you define constraints for.
[button1 setTranslatesAutoresizingMaskIntoConstraints:NO];
Unfortunately this leaves us with quite a lot of code if we have to place constraints on a set of UIViews. There are two ways to deal with this excess of code. We can create a category for UIViews that adds some simplified functions to deal with most of the constraints that need to be set. The second option is to use the Visual Format Language(VFL). The latter allows your to use a shorthand notation for your contraints, but more importantly, it allows you to set multiple constraints simultaniously.
To see this in action, lets have a look at a UITextView, if we set it the same way we did with our button we get a black screen, the UITextView will not show up, not even if we set some text. This is because the UITextView does not have have an intrinsicContentSize defined, which means that if you do not tell it what size it should be, it does not know and assumes 0. To fix this we need to set a height and width constraint like in the following example.
If we would write this in VFL it would look like this:
In the above example you will notice two things, unlike in the earlier examples here I add the UITextView to it’s superview before I set the constraints. We have to do this because within the constraint we refer to our superview implicitely instead of explicitely, which requires us to have a superview before setting the constraint. If you do not do this correctly the app will terminate throwing a NSInvalidArgumentException “Unable to interpret ‘|’ character, because the related view doesn’t have a superview”.
Secondly we now have a dictionary with the views we want to set constraints for, the VFL uses this to refer to the proper UIView object using the keys.
Most importantly though, we now have a shorter way to set our constraints, and we can set all the horizontal or vertical constraints on one UIView at once!