Articles, podcasts and news about Swift development, by John Sundell.

Layout Anchors

Published on 08 Jan 2019

Apple’s recommended way of defining layouts for most UIs is to use Auto Layout. Originally introduced all the way back in iOS 6 — at a time when we only had a small number of screen sizes to support — Auto Layout has undergone quite a lot of changes and improvements over the years, in particular with the introduction of layout anchors in iOS 9.

When building layouts using Auto Layout, we no longer manually calculate the positions and sizes of our views — and instead use a constraint-based API to have the system perform those calculations on our behalf, by evaluating the constraints we have defined. Since this is done automatically (hence the name) whenever a layout condition — such as the rotation of the device — changes, supporting all the various devices iOS and macOS runs on becomes much easier.

Since constraints are both very powerful and complex, the “raw” API for dealing with them is quite verbose and somewhat cumbersome to work with. For example, here’s how we’d make a label’s height match the height of a button, by defining a constraint ourselves:

let constraint = NSLayoutConstraint(
    item: label,
    attribute: .height,
    relatedBy: .equal,
    toItem: button,
    attribute: .height,
    multiplier: 1,
    constant: 0
)

Thankfully, layout anchors make the above kind of task a lot easier. Each UIView on iOS and NSView on macOS contains a series of anchors that can be used to automatically create constraints relative to other views. For example, here’s how the above constraint can be defined by using the heightAnchor of both views:

let constraint = label.heightAnchor.constraint(
    equalTo: button.heightAnchor
)

Much nicer, right? 😀 However, just like manually created constraints, the constraints created using layout anchors also need to be activated in order to be evaluated by the Auto Layout engine. That can either be done by setting the isActive property to true, or by using the activate API on NSLayoutConstraint to activate an array of constraints in one go:

// Both of these are valid ways to activate a constraint
constraint.isActive = true
NSLayoutConstraint.activate([constraint])

The beauty of Auto Layout is that it’s not only capable of making simple 1:1 relationships between metrics, but can be used to create really complex layouts as well. For example, here we position our button from before at the center of its parent view — while also giving our label a minimum width and placing it beneath the button:

NSLayoutConstraint.activate([
    // Place the button at the center of its parent
    button.centerXAnchor.constraint(equalTo: parent.centerXAnchor),
    button.centerYAnchor.constraint(equalTo: parent.centerYAnchor),

    // Give the label a minimum width based on the button’s width
    label.widthAnchor.constraint(greaterThanOrEqualTo: button.widthAnchor),

    // Place the label 20 points beneath the button
    label.topAnchor.constraint(equalTo: button.bottomAnchor, constant: 20),
    label.centerXAnchor.constraint(equalTo: button.centerXAnchor)
])

As you can see above, we’re free to mix and match anchors — for example constraining the top anchor of our label to the bottom anchor to the button — as long as they are within the same dimension. That is, we can pin top anchors to bottom ones and vice versa, but not any vertical anchors to any horizontal ones. The same goes for positional anchors such as leading, centerX, and right as well.

While layout anchors have vastly improved the user friendliness of Auto Layout, it’s still quite common to run into a few issues — especially in the beginning. So if your layout doesn’t initially look as you’d expect, here’s a few things to look out for:

Auto Layout will most likely continue to be a system that offers a great deal of power and flexibility, but at the cost of slightly increased complexity compared to when doing layout calculations manually. However, like with all things complex, it’s all about getting started — and things should eventually become much more clear.

Thanks for reading! 🚀