AutoLayout Revisited

My initial experiences with Apple’s iOS AutoLayout were pretty negative. Using Interface Builder’s (IB) ability to generate AutoLayout constraints automatically based on the positioning of views turned out to be frustrating, as it would generate constraints that were incompatible with iOS 7. As iOS 8 has only been out for a few weeks, I definitely want to keep supporting iOS 7 in my app. But Xcode 6 generates these incompatible constraints anyway, even though the deployment target is iOS 7. Furthermore the automatically generated constraints don’t really do what I want, such as keeping views centered on the screen when the screen enlarges from iPhone size to iPad size. So I was forced to go back to the drawing board and really try to understand how AutoLayout works.

My impetus for all this was my desire to upgrade one of my apps from an iPhone only app to a Universal app — optimized for display on both the iPhone and iPad. The app (EP Mobile) has a big storyboard and many different views. By using AutoLayout I hoped to avoid having two different storyboards, one for iPhone and another for iPad, and just use one storyboard for both devices. I decided to check the Use Size Classes option in IB. Supposedly this allows for designing separate layouts in a single storyboard for different size devices. As it turns out, this was not helpful, as apparently this feature only works on pure iOS 8 apps. Moreover, as the compiler seems to generate code for each possible device, building your app after making a change in the storyboard takes much longer than it did before enabling this option. After a while I grew tired of this and decided to turn off Size Classes. However trying to do this resulted in a warning dialog from Xcode that stated a lot of nasty things that might happen (I hate dialogs that use the word “irreversible”) and so I decided to just live with the longer build times.

I watched some YouTube videos on AutoLayout that were helpful (here and here), but in truth the best way to learn AutoLayout is to play around with it. Take a view, clear any constraints that are there, put the subviews where you want them, and then add your own constraints manually. While doing this, ignore warning messages from Xcode about ambiguous constraints and misplaced views. Ignore the yellow and red lines that show up on the screen indicating these errors. Until you have completely specified all the constraints needed to  determine unambiguously the location of the subviews without conflicts, these warnings will show up. Prematurely asking IB to Update Frames before all the constraints are specified will make the subviews jump around or disappear. Unfortunately even when all constraints are specified and correct, the yellow warnings don’t go away. IB is not capable of automatically applying your constraints and misplaces your controls in your views whenever you change constraints. Sometimes it misplaces controls even when you are just changing the storyboard metrics from one size to another. Update All Frames then puts everything where it belongs.

One way to start is by putting constraints on heights and widths of controls that you don’t want to resize when the screen size changes or the device rotates. Note that some controls, such as buttons, have an intrinsic size based on the button label, and it is not always necessary to add specific constraints to these controls. However, it looks to me like the system will ignore the intrinsic size at times, especially if you are trying to do something fancy with constraints, and your button will grow to a ridiculous size to satisfy your constraints. So it doesn’t hurt to specify width and height constraints manually even in these controls. Of course if you want controls to expand in one direction or another, don’t specify a constraint in that direction.

Next step is to align controls that are lined up horizontally. You can select multiple controls and then align their vertical centers using Editor | Align | Vertical Centers on the menu. If there are rows of controls like this you can take the leftmost control and working from the top to bottom, pin each row to the row above (or to the superview for the first row) to make sure there is vertical separation between the rows. Finally, usually you want your controls to be centered on the screen, even when using different screen sizes and with rotation. If you have one wide control, such as a segmented control or a large text field, you can horizontally center that control. You can then pin the leading edge of that control to the leading edge of the superview (i.e. the window) and that view will grow as the screen width increases. Aligning the leading edges and trailing edges of the other controls to this view will allow the whole set of controls and expand and contract with the width of the screen. If you have rows of controls, you may still need to put constraints between individual controls to control the horizontal distance between them.

One issue I noticed was that, while it’s nice to have controls expand to fill the screen of the iPhone when going from the small iPhone 4s to the 5.5 inch iPhone 6, sometimes the controls get too wide when viewed in landscape mode or on the iPad if they are just pinned to the superview leading edge.  For example, this segmented control is centered horizontally and vertically in the superview, and the leading edge is pinned to the superview leading edge.

Screen Shot 2014-10-01 at 10.03.52 AM
Segmented control has centering constraints and is pinned to the leading edge of the superview.

On the iPhone 4s, with rotation the view remains centered and enlarges when the device is rotated.

iPhone 4s portrait
iPhone 4s portrait
iPhone 4s landscape.  Control remains centered and expands to fill screen.
iPhone 4s landscape. Control remains centered and expands to fill screen.

To show the flexibility of AutoLayout, we can limit the expansion of the segmented control to a maximum we select, by making the width less than or equal to a value (in this case 350) and lowering the priority of the pinning of the leading edge to the superview. This achieves the desired effect.

Width of control constrained to ≤ 350 and priority of pinning to left margin decreased to 750
Portrait view is unchanged, but landscape view (shown here) limits the width of the control to a max of 350
Portrait view is unchanged, but landscape view (shown here) limits the width of the control to a max of 350

You can do a lot with AutoLayout just using IB if you are patient and try out various effects. You can do more by attaching your constraints to outlets and manipulating them in code. It is unfortunate that some glitches in the implementation of AutoLayout in Xcode 6 Interface Builder make using AutoLayout more frustrating than it needs to be.  To those who are discouraged like I was by AutoLayout, I urge you to keep experimenting with it.  The a-ha moment will come and it will be worth it.

By mannd

I am a retired cardiac electrophysiologist who has worked both in private practice in Louisville, Kentucky and as a Professor of Medicine at the University of Colorado in Denver. I am interested not only in medicine, but also in computer programming, music, science fiction, fantasy, 30s pulp literature, and a whole lot more.

2 comments

  1. I have to be honest, I didn’t read the whole post, a bit technical for me as I don’t know anything about iProgramming. But now that you’ve used both and gotten a little more into the apple version, which is better at auto layout Apple or Android?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.