Working With Auto Layout And Layout Anchors
Creating layout constraints in code is not pretty. It is not always intuitive and it is verbose, even if you decide to use Apple's Visual Format Language.
In iOS 9 and OS X El Capitan, Apple introduced the
NSLayoutAnchor class to make working with layout constraints in code easier and more intuitive. I must admit I was a bit skeptical at first. Even the Visual Format Language makes it difficult to manage constraints, how would another API make it easier.
Fortunately, I was wrong. The
NSLayoutAnchor class makes creating and managing constraints almost painless. In addition to the
NSLayoutAnchor class, Apple added a number of properties to
NSView. The result is pretty nice.
How It Works?
There are a few things you need to know about the
NSLayoutAnchor class. First, you are not supposed to explicitly create instances of the
NSLayoutAnchor class. Instead, you obtain a reference to a layout anchor from the
NSView you are working with. How that works becomes clear in a few minutes.
NSLayoutAnchor class can be considered an abstract class. In practice, you interact with one of its subclasses:
NSLayoutDimension: This subclass is used to create layout constraints that describe the width and height of a view.
NSLayoutXAxisAnchor: This subclass is used to create horizontal layout constraints.
NSLayoutYAxisAnchor: This subclass is used to create vertical layout constraints.
Third, the existing APIs for programmatically creating layout constraints do not warn you if you are defining invalid layout constraints. The
NSLayoutAnchor class adds type checking to the mix, warning you about potential problems at compile time. While this is not 100% waterproof, it certainly helps creating the right set of constraints and facilitates debugging Auto Layout issues.
But what is a layout anchor? If you take a look at the new properties of
NSView, you probably understand what they are. These are the available layout anchors for
These properties correspond to an
NSLayoutAnchor subclass. For example, the
bottomAnchor property of a view is of type
But how do you create a layout constraint using a layout anchor? Creating layout constraints with layout anchors is almost identical to creating layout constraints in Interface Builder. Take a look at the following example.
// Create & Add Layout Constraint let leadingConstraint = subview.leadingAnchor.constraintEqualToAnchor(superview.leadingAnchor, constant: 8.0) // Activate Layout Constraint leadingConstraint.active = true
We access the
leadingAnchor property of
UIView instance, and create a layout constraint by invoking a factory method,
constraintEqualToAnchor(_:constant:). This method accepts two arguments:
- a constant of type
leadingAnchor is of type
NSLayoutXAxisAnchor, we need to pass in a
NSLayoutXAxisAnchor instance as the first argument of
constraintEqualToAnchor(_:constant:). The resulting layout constraint is automatically added to the
view instance, but note that we need to activate it by setting its
active property to
I am sure you agree that the above example is easy to read and understand. Adding layout constraints using the
NSLayoutAnchor API is clear and concise. It is great to see that Apple continues to invest in improving Auto Layout. This reaffirms that Auto Layout is the preferred way to create user interfaces for modern Cocoa applications.
I would like to end this tutorial by rewriting the layout constraints we added in the last lesson Auto Layout Fundamentals. Download the project we created from GitHub and open it in Xcode.
Open ViewController.swift and navigate to the implementation of
setupContainerView(). The first layout constraint we add in this method uses the
// Width let constraintWidth = NSLayoutConstraint( item: containerView, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 320.0 ) // Add Constraint containerView.addConstraint(constraintWidth)
NSLayoutAnchor API makes this more readable and more concise. This is the equivalent using the
containerView.widthAnchor.constraintLessThanOrEqualToConstant(320.0).active = true
We access the
widthAnchor of the container view and use it to create a layout constraint that constrains the width of the container view to be less than or equal to
320.0 points. Because
constraintLessThanOrEqualToConstant(_:) returns an
NSLayoutConstraint instance, we can activate the layout constraint by setting its
active property to
The next layout constraint centers the container view horizontally in its superview. This is what that looks like using the
// Center let constraintCenter = NSLayoutConstraint( item: containerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0.0 ) // Add Constraint view.addConstraint(constraintCenter)
NSLayoutAnchor, we can replace this with one line of code.
containerView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
For the next two layout constraints, we access the view controller's top and bottom layout guides. The idea is similar, though. The
bottomLayoutGuide properties conform to the
UILayoutSupport protocol. Through this protocol, they expose three properties:
containerView.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8.0).active = true containerView.bottomAnchor.constraintEqualToAnchor(bottomLayoutGuide.topAnchor, constant: 8.0).active = true
We need to modify the priority of the layout constraints for the last two layout constraints. This is not a problem as you can see below.
let constraintLeading = containerView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 0.0) constraintLeading.priority = 750.0 constraintLeading.active = true let constraintTrailing = containerView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: 0.0) constraintTrailing.priority = 750.0 constraintTrailing.active = true
Layout Anchors Rock
I don't know about you, but I am pleasantly surprised with the addition of the
NSLayoutAnchor API. As I mentioned, it is great to see that Apple continues to invest in Auto Layout with the addition of stack views and layout anchors. In the past, I tried to avoid adding constraints in code because it is verbose and often confusing. With the addition of layout anchors, that has changed.