Joe Conway

iOS Autolayout Fun Facts and Tips

July 15, 2013

In this post, I wanted to have a light description of some of the iOS Autolayout fun facts and tips that some iOS programmers might not already know.

Have you ever stared at the tracking information for your new MacBook, hitting refresh every 5 minutes? It is infuriating. But the fact that a MacBook can come off the assembly line somewhere in China and end up at my doorstep a few days later is a serious work of mathematical art.

Consider all of the issues in optimizing the pickup and delivery of your next MacBook: a company like FedEx has to factor in the number of trucks they have, the amount of goods that can fit on those trucks, the cost of gas, the distance from their nearest distribution center to your doorstep, and so on. If they were to just shoot from the hip and deliver things in whatever order their manager thinks is best, they would run out of money or your shiny laptop wouldn’t show up. Instead, they have to rely on a field of math called linear programming to determine the way they operate.

Linear programming (which has nothing to do at all with programming in the way we think of it, but instead, is a synonym for linear optimization for math folks) is used to solve equations for which there are a lot of variables that depend on each other. FedEx needs to minimize some of these variables (like the amount they spend on gas) and maximize some others (like how many items can be delivered in a day).

Because there are so many variables involved and each of their ranges is so vast, a human being couldn’t find the optimum value for each of the variables to minimize their costs and maximize their production, but a linear objective function can. A human being can set up some constraints for each variable, like a truck can travel up to 200 miles in a day and gas costs $3.50 today. Each of these constraints is a simple linear equation, like travel <= 200.0 and gas = 3.50. A linear objective function is a combination of all of these equations solved at once that finds the optimum value for each unsolved variable.

What does this have to do with autolayout? The system is one big linear objective function. Each NSLayoutConstraint you create is just one linear equation in a linear objective function. For example, one constraint could be view.left = anotherView.right + 10 and another could be view.width >= 20.

When a view is being laid out, the linear objective function is evaluated to determine a minimum and maximum value for each part of a view’s frame. A valid set of constraints is when every variable has a maximum equal to its minimum; that is, there is only one solution to satisfy every constraint in a view hierarchy. (If there is more than one solution to the function, you have an ambiguous layout, if there isn’t a solution, you have conflicting constraints and you get an exception.)

Because of the way linear objective functions are evaluated, there isn’t an order that the constraints are computed in. So if that was how you were approaching constraints, get that out of your head: an equation like view.top = anotherView.bottom + 10 doesn’t mean that you find the value of anotherView’s bottom first and add 10 to get view’s top. It is just one more equation that has to be satisfied for your layout to work; both of these variables can change depending on the other constraints.

Of course, the math behind all of this is really complicated. The implementation is less complicated, but let’s not pretend it is easy at first. So, here is what I recommend:

1. Use autolayout only when you need to

Autoresizing masks aren’t deprecated or obsolete. Just like Core Data didn’t make archiving obsolete nor did blocks make delegation useless, you will still use autoresizing masks. Many iOS applications – while they may have pretty artwork – are pretty simple layout wise. You have a table, it has some cells, there is some stuff in the cells, there are some buttons above it and maybe a duck. Well, probably not a duck, but do you really need it to keep the table view on the bottom of the screen and the buttons as a header on top? Does a cell with two text fields need it to keep them aligned with each other? Will rotating the device, putting into a navigation controller or running it on an iPhone 5 really change the layout of this view? No, so no need for overkill.

2. The keyboard shortcut you need to know about

Manipulating the layout of a user interface in a XIB is, let’s face it, a cruel joke. Once a view is dropped into a XIB, you get all kinds of constraints that make sense for the position of that view where you dropped it – but not where you eventually want it. On top of that, Xcode enforces the constraints that apply to your views when manipulating them in the XIB – got a label that is pinned to match the size of the button below it? Xcode may not let you drag the resizing controls for that label.

Unless you hold the command key. The most useful but under-documented feature of autolayout is holding the command key while manipulating views in a XIB file. While holding the command key, Xcode lets you break any constraint applied to a view while resizing it.

3. Stop thinking about frames

When laying out an interface, we typically think about the frame of a view. Stop. The frame of a view is an absolute position and size. That doesn’t make sense when a view should lay itself out relative to the rest of the screen. Want to think about the size of a view? Think about its intrinsic content size. Want to think about the position of a view? Think about its offset from other views in the interface. A button doesn’t have a frame of {20, 40, 200, 40}, no, it is 10 points below a label, centered on the Y axis of its superview and wants to fit its title comfortably.

4. Change constants, not constraints

One of the more useful (and difficult) features is the ability to make dynamic user interfaces. If your model changes and a new button needs to move into place, don’t delete the current constraint and create a new one. Finding constraints at runtime is not straightforward, but modifying the constant of a constraint is very simple. If a button needs to slide in from the right side of the screen, create a constraint where initially button.left = superview.left + 400. When it comes time to slide it in, change the constant so that the equation is button.left = superview.left + 200. Bonus: animation comes for free:

[[self buttonEdgeConstraint] setConstant:200];
[UIView animateWithDuration:0.5 animations:^{
    [[self button] layoutIfNeeded];
}];

5. Understand the limitations and bugs

Right now, setting up constraints in a XIB file for a UITableViewCell doesn’t do what you expect it to do: each constraint thinks that the superview is the instance of UITableViewCell, not the cell’s contentView. This means that you will never get what you expect. Instead, turn off autolayout for a UITableViewCell XIB and either set up constraints programmatically or use autoresizing masks.

When modifying a XIB, Xcode won’t let you set up constraints that are invalid (for the most part). That means if you have a constraint you don’t want, you may have to add another constraint before you can delete it.

The thickness of the constraint line in the XIB is your key: a thin blue line is a required constraint, whereas the thicker blue line is a user constraint. Required constraints can’t be deleted, otherwise, the layout would become invalid. When a set of constraints over-qualifies a view’s frame, the constraints that aren’t necessary automatically become user constraints that can be deleted. For example, if you plop a text field onto the screen, it may set up a left and right edge constraint. If you don’t want the right edge constraint, pin the width of the text field – this makes the right edge and width constraints redundant. Then you can delete the right edge constraint and the width constraint will become required.

Published July 15, 2013
Joe Conway

CEO & Founder at Stable Kernel

Tags:

0 Comments

leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *