The new larger iPhones and iOS 8 are here. Xcode 6, upgraded to deal with these new beasts, is also ready for download. Anyone who has written apps designed for the iPhone has to make sure their apps run on these new devices and the new iOS. Previous iPhones had two different heights (3.5 and 4 inches) but were the same width. The new iPhones are not only longer, but are wider. With the previous devices, it was relatively easy to design for the slightly different heights. Now the developer has to deal with layouts that work with many different aspect ratios. If your app is a universal app (i.e. runs on iPhone and iPad) there are even more sizes that you must deal with.
Anticipating this, Apple, a few versions back, introduced AutoLayout to take the place of their older layout system using springs and struts. With AutoLayout, you place your widgets where you want and, automagically, they retain their relationship with different screen sizes or when rotating the screen. Or so it’s supposed to work in theory.
As it turns out, AutoLayout introduces its own set of headaches which rank up there with your worst migraines. Let’s begin.
Intrinsic AutoLayout
If you have AutoLayout turned on (which is the default now), any prior layouts you have, whether you used springs and struts or absolute layouts, will be converted to the AutoLayout system. Any new layouts will use the AutoLayout system automatically. When placing widgets, the dotted blue guidelines will have more meaning than they did before. For example, if you center a widget using the blue lines, that widget will appear centered with different device sizes or with rotation. This is called intrinsic AutoLayout, and the effect is added at run-time. That means, somewhat surprisingly, that when you change device size using Interface Builder (IB), the layout won’t look right.
But if you run the simulator, the layout will look fine.
Hopefully that will be true when running on the new hardware itself, but since I don’t have an iPhone 6 I can’t test this. The moral of this story is don’t start fiddling with your layout because it looks weird on IB. Run your app on the simulator at each size (and on hardware if you have it). Most of your layouts will look OK on the larger iPhones due to intrinsic AutoLayout.
Explicit AutoLayout
If you need to alter what is produced by intrinsic AutoLayout, you have to manually add constraints. Constraints are a rigorous description of a relation between two views. Examples include centering a widget in a superview, fixing a widget’s height, but allowing width to change, and setting a fixed distance between two widgets. Constraints are actually pretty cool in theory and can be added in code. But using IB, once you start adding constraints all hell breaks loose.
Suppose everything about your view looks good, but you want the bottom of your widget to be pinned to the bottom of the view it is contained in. So you add that one constraint. With that one action you have blown away the intrinsic AutoLayout system and IB will inform you that you have all sorts of ambiguous heights and widths. So if you add any constraints in IB, you have to add constraints to all the widgets. There is a menu item to do this, and suddently there are about 50 constraints added to your view. And here you hit your first surprise. Even though you are targeting your app for iOS 7, Xcode adds constraints that are incompatible with iOS 7!
Constraints can’t be relative to the superview margin in iOS 7 but IB blithely adds them anyway. So you go through the constraints, one by one, edit the ones that have “relative to margin” checked, which ends up changing the appearance of your view. It is then that you discover that changing a constraint tends to mess up your view or mess up the other constraints. You often get a message saying that the view at runtime will not appear the same as it appears on IB. I found that pressing Option-Command+= fixes that. It is easy to get frustrated when editing constraints in IB. The “Clear Constraints” menu item is your friend here, so you can start all over.
AutoLayout and ScrollViews
It gets worse. If you have any ScrollViews in your app, with AutoLayout they appear to work on the simulator, but they don’t work on actual hardware! Turning off AutoLayout fixes this, but DON’T DO THIS! You cannot turn off AutoLayout for a single view. It goes off for your whole storyboard. And your carefully laid out and tweaked constraints disappear. Version control is your friend here. Using AutoLayout and ScrollViews is complicated, as you can see from this. After reading this and playing around, I was able to get my views to scroll by pinning the ScrollView to the superview on all four sides. Interestingly, when setting up a ScrollView with AutoLayout, you no longer have to add any code about the ScrollView as was necessary before (you would have to add the content size of the ScrollView for it to work). But again, this is a real trap, especially as if you do it wrong, it looks like it is working on the simulator but won’t work on an actual device.
And so…
Handling resizing and rotation is one thing that is a lot easier when developing for Android than for Apple iOS. AutoLayout has a steep learning curve, and others have had these same problems (search for AutoLayout on StackOverflow, or see this. I hope that Apple can improve this experience. I like the concept of AutoLayout, but the devil is in the details.
Update: Since writing the above, I have gone back to basics with AutoLayout and made a concentrated effort to understand the system. I don’t think the AutoLayout system is bad; however, using AutoLayout in Interface Builder can be rough going for the reasons given above. AutoLayout itself though is very flexible and I do think you can do just about anything you want with it if you understand it well. I am trying to achieve this level of understanding. I have found that the best way to learn is to play around with a view, adding constraints and seeing what happens. I have also discovered the new feature of Size Classes in Interface Builder in Xcode 6 which makes developing a Universal App using a single storyboard much easier. So I’ll keep plugging away until I have conquered this beast.
With the custom sizes that you can now do in a single storyboard in IB I have found that if you create a custom size, and set the constraints for that then your constraints in the main view go all to pot, and visa versa. I don’t know if you have come across this at all?
Peter
No, but I haven’t tried it. I’ve found it doesn’t take much to trash your constraints in IB.