iOS Auto Layout: A Failure in Execution

Auto Layout was supposed to be Apple’s answer to providing flexible and self adjusting UI layouts across devices of varying screen sizes. I truly believe Auto Layout could be the answer. Unfortunately, Apple’s execution with Auto Layout has been abysmal.

I don’t have any reason to believe that the core technology of Auto Layout is flawed. At its core, Auto Layout solves a system of linear equations defined by constraints. I’m going to assume that this linear equation solver works as advertised and works quite well, maybe even perfectly.

Unfortunately, Apple introduced a number of other settings along with Auto Layout that, in practical terms, dumped a combinatorial problem into developer's laps. Each year, Apple introduces seemingly small updates or changes related to Auto Layout that have made the problem increasingly worse. It often isn’t clear when one of these settings should be changed or what the results will be. A lot of time can be wasted trying out different settings until a layout gets close enough to the goal to give up.

Here are some of the issues I’ve found while using Auto Layout…

 

translatesAutoresizingMaskIntoConstraints
This is a switch on a view that is supposed to bridge the old way of doing layout (aka springs and struts) with Auto Layout. Its default value changes depending on whether a view is created in code or in Interface Builder. Mysteriously, its behavior has changed over time, such that in some code, sometimes, I could fix differences in layout that arose between versions of iOS by including a single line of code like this:

view.translatesAutoresizingMakeIntoConstraints = IS_IOS_8_OR_GREATER;

This trick couldn’t be applied universally, which leads me to believe there is some interaction between some types of subviews and their constraints and the value of translatesAutoResizingMaskIntoConstraints on the parent view.

That’s crazy. And seemingly unpredictable.

 

UITableViewAutomaticDimension
This transfers responsibility for table cell height calculation over to Auto Layout so that developers don’t have to write the code to calculate the height themselves. Online tutorials and Apple present this as a one line wonder of coding that literally requires no other code or settings (although, it is usually presented with one other line of code related to an estimated cell height). One line of code like this:

tableView.rowHeight = UITableViewAutomaticDimension

I’ve taken out code that works perfectly well and that uses a commonly known boiler plate function for calculating cell height, set this one line of wonder code in its place, and had my table view cells display with incorrect heights and vertically stretched or truncated UILabels. Every time I think I’ll give it another try, I kick myself for wasting my time. I’ve seen other developers get this to work, but no one could explain why it worked in some cases and not others. It works sometimes.

Here is a blog post from a developer that submitted a bug report to Apple against iOS 8 regarding this issue. Apple asked the developer to verify a fix against iOS 9 and the bug still existed.

 

preferredMaxLayoutWidth
Developers need to provide this value when using multi-line UILabels or UITextViews. Starting in iOS 8 developers could instruct Auto Layout to automatically set this value. I’ve tried letting the system set it automatically on several occasions, as recently as last week. The result was a view that extended off the edge of the screen. I fixed it by setting the preferredMaxLayoutWidth explicitly in code. Again, allowing the system to automatically set preferredMaxLayoutWidth is supposed to “just work”, but it doesn’t. It works sometimes.

 

Size classes
When this was first introduced it seemed like a great idea. Now, I'm unconvinced. Size classes are too abstract and disassociated from reality. In reality, all layouts tie back to pixels and points. If I need to size things relative to each other and the only metric I have of those objects is their size in points, the abstract notion of a Compact or Regular layout size doesn’t help me. If I need to do layout based on the size of a string, which can only be measured in points, a size class doesn’t help.

The introduction of the 12.9” iPad Pro with no additional size class to classify it already makes the notion of a Regular size screen unusable. Maximizing readability of a line of text often cannot follow the same layout rules on both a 9.7” and a 12.9” iPad.

I think size classes are just a mechanism for making constraints editable as sets in Interface Builder. Instead of allowing developers to define sets of constraints according to dimensions, the number of potential constraint sets is limited by tying the number of sets to the number of defined size classes (squared). I wonder if size classes have introduced more complications than simplification though.

 

Content Hugging and Content Compression priorities
At the end of the blog post cited above, the developer says that his labels should have been laid out correctly because they were assigned a content hugging priority of 1000. This is an incorrect interpretation of the content hugging priority values. But it is a misinterpretation that Apple created.

Content hugging and compression priorities are values that help to break ties between Auto Layout constraints that conflict. Unfortunately, Apple has associated descriptors to a few values in Interface Builder that only serve to confuse developers. These values range from 1 to 1000. The value 250 is labeled as “Low” priority, 750 as “High” priority, and 1000 as “Required”. These labels lead many developers to set every priority to 1000, since any constraint made really should be required, right? A priority of 1000 really means that if there is a conflict, the priority marked at value 1000 is more likely, but not guaranteed (e.g. if the conflict is with another priority of 1000), to win. This is not the same thing as "required".

When developers set all constraints to required, it makes it very difficult to make changes to a layout later.

 

Interface Builder and real time ambiguity feedback
Interface Builder (IB) tries to solve a constraint system every time you make a change to a constraint in its WYSIWYG editor. So, IB suggests fixes to a set of constraints that aren’t finished yet. It offers to automatically fix the issues, usually by adding more constraints. Developers that fall for IB’s offer to fix the constraints often end up with views that are overly constrained, which can be difficult to change later on. When updating views like this, I usually find it easier to just start over.

 

Intrinsic content size
UIImageView intrinsicContentSize in particular, is problematic. UIImageViews set their intrinsic content size equal to the size of their image. This is never the behavior I want. I don't want images driving the layout. I want images placed within a layout according to the space given to them. When possible, I'll set size constraints on image views to override the intrinsic content size. But, if an image should be sized relative to other UI elements, intrinsic content size makes it unnecessarily difficult. Depending on when constraint updates are made that pick up my settings, the user may or may not see the error (an image view that is as big as it's image) before it is corrected. Peppering code with attempts to hide the error is maddening.

 

Conclusion
These are just the issues that I could think of in one sitting at my keyboard. In Auto Layout we have a number of settings that sometimes work, may behave differently across versions of the operating system, and in combination make it extremely difficult to determine which setting should be changed to fix an issue. Even if there are small tricks to make the factors above work correctly all the time, I shouldn’t have to figure out small hidden implementation details and how they work in combination when Apple implies all of them should “just work”.

I estimate that I’ve spent a month of development time in the past year dealing with Auto Layout issues. An entire month out of twelve! That doesn’t include initial setup of constraints and code to handle constraint changes. That’s a month of development time spent scratching my head and cursing at my screen for layout errors that I can’t explain. The solution usually involves a work around that I know shouldn’t be necessary, but is because of issues like those listed above.

I'm hopeful, but not optimistic, that Apple will fix these issues. The issues are out there in millions of lines of code. Apple has committed to them. Backtracking, even to simplify Auto Layout and give developers a more stable method of specifying layout, is probably not something Apple would pursue.

In future posts I may address some techniques for dealing with the short comings of Auto Layout. But it would be better if the issues were fixed at the source and those posts were made unnecessary.