It took a while, but I finally feel I can get auto layout to do the basic things that I need it to do.
I’ve put together a test app with a few different layouts – each in a separate view controller.
My approach to work with auto layout has been:
- Define the layout and behavior of the objects to put in the view
- Add one by one the elements to the view
- Set the constraints
- Run the app to test the view
- If there is a conflict, fix the conflict
- Have a breakpoint in viewDidAppear, and check for ambiguous constraints with
po [[UIWindow keyWindow] _autolayoutTrace]
- If there is no ambiguous layout, check the behavior with orientation and device changes (iPhone 3.5/4, iPad)
- Keep adding the views and checking for conflicts and ambiguous constraints
Some tips to debug layouts
po [[UIWindow keyWindow] _autolayoutTrace]shows the full hierarchy of views in the app. It also indicates when a view has an ambiguous constraint
(lldb) po [[UIWindow keyWindow] _autolayoutTrace]
$0 = 0x08d46830
| | *<UITransitionView:0x8e4da90>
| | | *<UIViewControllerWrapperView:0x8e5ac70>
| | | | *<UIView:0x8e57ef0>
| | | | | *<UIScrollView:0x8e574b0>
| | | | | | *<UIView:0x8e57b70>
| | | | | | | *<View1:0x8e59fb0> - AMBIGUOUS LAYOUT
| | | | | | | *<View1:0x8e5a6d0> - AMBIGUOUS LAYOUT
po [0x7549380 constraintsAffectingLayoutForAxis:0]shows the constraints that are used by the auto layout manager to compute the layout for that view (0x… is the address of a view that you get when printing out the trace). The parameter is
0for horizontal axis and
You get a list such as
<NSLayoutConstraint:0x75498d0 H:|-(8)-[View1:0x7549380] (Names: '|':UIView:0x7546df0 )>. The list of view names is unfortunately not very useful for debugging iOS constraints – it’s supposed to display a string identifier that you can set for each view, but so far it works only for Mac development. For this reason, when getting stuck debugging the layout for a complex UI, it might make sense to (temporarily) subclass the views so that you at get a little bit more information when printing this debug information.
- Conflicting constraints trigger an exception when they are added to the view. Conflicting constraints are solved by the auto layout manager by removing one or more constraints until the manager can compute a layout:
Will attempt to recover by breaking constraint. Forgetting to set
translatesAutoresizingMaskIntoConstraintsto NO for views created programmatically and added with constraints to a view often cause this.
- Conflicting constraints mean that there is no solution to compute the UI layout. For instance, the width of a view is required to be 100pts and 200pts.
- Ambiguous constraints mean that there might be several solutions to a layout. For instance, a view could be either 100pts or 200pts. The auto layout manager picks a solution, but might not pick the same one every time.
- Other debugger:
po [0x123456 hasAmbiguousLayout]which prints 0 or 1, and
po [0x123456 exerciseAmbiguityInLayout]which makes the auto layout manager pick a different solution for an ambiguous layout.
- The minimum set of constraints required to compute a layout without conflicts or ambiguities is the one that is sufficient for placing and sizing each view. The auto layout manager needs enough information to know where to place the view (x, y), and to size the view (width, height).
- Views that have an
intrinsicContentSizeprovide themselves some information about their sizing (width and height, or either). For instance, for a view that has an intrinsic content height such as
UIButtonobjects, you do not need to provide a constraint on its height. How that height is used depends on the view’s
contentCompressionResistancePriority. If you set a constraint on the height of that view, the priorities (of the intrinsic content size and the additional constraints) will determine which constraints are used.
- If you add a view but you do not see it, it’s most likely because
0is a possibly solution for the width and/or the height of that view. It helps when debugging to give those view a minimum size.
- Priorities are mighty important. They are necessary to express things such as: the view should fill in its superview, but if its content is bigger than the superviews size, it should get bigger.
- Changing priorities might be the solution to solving ambiguous constraints. A constraint is required if its priority is 1000. Anything below is not. Priorities work (I think) relatively to each other. Which means that what actual values you use do not matter as much as their relative difference. Although iOS defines 4 constants, so it might be easier to base your priorities on them:
UILayoutPriorityRequired = 1000, UILayoutPriorityDefaultHigh = 750, UILayoutPriorityDefaultLow = 250, UILayoutPriorityFittingSizeLevel = 50.
- Working with constraints in a .xib file is tedious. A simple click or drag will trigger updates on the constraints easily break your constraints. You end up linking constraints to IBOutlets to remove them and add new ones, or modify their constant.
- I used to really like .xib files and thought that they were better to code UIs because unlike UIs created programmatically, .xib files give a good idea of how elements are placed relatively to each other. When using auto layout, .xib files make it a lot harder and inconvenient. The ASCII style of constraint creation provides enough visual information about the placement of UI elements. For instance:
- I haven’t managed to find a way to set constraints properly on scrolling elements in the interface builder. That’s why if I use IB, I have a content view as a top level object in IB, and I create the scrollview in viewDidLoad.
Do NOT set frames and sizes on views with auto layout. Remove all
My personal conclusions so far are:
- I can’t believe how long it took me to get a grasp of auto layout. Now that I’m getting the hang of it though, it’s not that hard and I like the flexibility of it. But there is still a lot I have to learn about it (such as animations, or the
constraintsWithVisualFormat:options:metrics:views:) and I’m not sure whether it’s a good idea to fully adopt it or not – time will tell.
- Visual format for creating constraints is great.
- Animating views through constraints works well enough for simple translations (panning, resizing). For animations triggered by gestures (animating a view on a panning gesture event) or for rotations/3D effects, it makes the animation sluggish and the programming tedious (removing/adding constraints). So I expect I’ll be using a mix of views with no auto layout, and some with.
- Auto layout makes it very easy to size views based on their natural / intrinsic content size.
- .xib files become less useful. Maybe it’s a good thing if we can get rid of .xib files in projects. If you use AppCode, then no need to switch to Xcode to code your UI.
- I hope Apple won’t make drastic changes to Auto layout
- Watching the WWDC videos of the talks about auto layout really helped. It is worth investing the few hours in watching them.
There are a couple more use cases that I will probably add to the test app: for instance, mixing views with auto layouts and views without, and examples of animations of constraints’ constants.
Any suggestion and sample code welcome!