Ryan Jones

Anchoring with NSLayoutAnchor and Auto Layout

The NSLayoutConstraint class has been Apple’s recommendation for layout, as they create relationships between views, parent views, and child views, therefore explicitly setting the frame property is not needed. This also has benefits of helping with accessibility, multiple device screen size support, and helping with views in different orientations. I’m little late to the party with iOS 9’s NSLayoutAnchor, which simplifies the wordy NSLayoutConstraint instantiation, with a simple-to-use API.

As Apple’s documentation states:

// Creating constraints using NSLayoutConstraint
NSLayoutConstraint(item: subview,
                   attribute: .Leading,
                   relatedBy: .Equal,
                   toItem: view,
                   attribute: .LeadingMargin,
                   multiplier: 1.0,
                   constant: 0.0).active = true
NSLayoutConstraint(item: subview,
                   attribute: .Trailing,
                   relatedBy: .Equal,
                   toItem: view,
                   attribute: .TrailingMargin,
                   multiplier: 1.0,
                   constant: 0.0).active = true

// Creating the same constraints using Layout Anchors
let margins = view.layoutMarginsGuide
subview.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor).active = true
subview.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true

The NSLayoutAnchor has methods that start with constraint… that return a newly created NSLayoutConstraint and they add the created constraint the views of the parameters of those constraint… methods.

The view to add the constraints to was obtained from the layoutMarginsGuide, which is an object that contains pre-defined guides on every view. Adding guides to this UILayoutGuide object, can be especially helpful in creating guides without the need to create “spacer views” (which is not covered in this post).

Global Anchors

Upon first working with this API there appears to be duplicate anchors, leftAnchor vs leadingAnchor, and rightAnchor vs trailingAnchor in the layoutMarginsGuide. These are not duplicate anchors, instead the leadingAnchor and trailingAnchor combo will automatically adjust your layouts for left-to-right languages or right-to-left languages based on the device locale.

For example, labels describing a text field could be on the left of a text field for English and on the right for Arabic – yet both would be spaced the same distance from each other irrespective of their locale.

Adjusting Anchors

Anchors are always a specified distance away from the edges, the defaults. These are defined in the view’s layoutMargins, which are UIEdgeInsets. If these are changed the anchors will respect that. However in the example code mentioned above the line:

let margins = view.layoutMarginsGuide

Will only show your new edge insets as long as the layout has been updated. Otherwise these values are the originals. If constraints get added with the code above in viewDidLoad, the views will be with the old values, this happened in my testing. A way around this was to use the anchor from the view directly.

subview.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true

Anchors Away!

Anchors can only be set in code currently. Though when you have to create constraints, being able to do them with anchors helps you write a lot less and more readable code.