A frequent complaint with Auto Layout is how verbose and unreadable the syntax is for programmatically creating constraints. Fortunately iOS 9 has done a lot to improve things. Stack Views have removed the need for us to create many of the constraints in typical layouts. Overlooked by comparison, but just as useful, was the introduction of layout anchors and layout guides. From the Apple Auto Layout Guide:
You have three choices when it comes to programmatically creating constraints: You can use layout anchors, you can use the NSLayoutConstraint class, or you can use the Visual Format Language.
I will look at layout guides another time but for now here are my notes on using layout anchors to create constraints in code without the pain:
Last updated: Nov 10, 2022
Creating Constraints the Painful Way
First a reminder of the painful way to create constraints using the NSLayoutConstraint
class methods. Assume we have a stack view we want to pin to the left and right margins of the top-level view of a view controller:
NSLayoutConstraint(item: stackView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leadingMargin,
multiplier: 1,
constant: 0).isActive = true
NSLayoutConstraint(item: stackView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailingMargin,
multiplier: 1,
constant: 0).isActive = true
We can also pin the stack view below the top layout guide so that it does not get hidden by a navigation bar:
NSLayoutConstraint(item: stackView,
attribute: .top,
relatedBy: .equal,
toItem: topLayoutGuide,
attribute: .bottom,
multiplier: 1,
constant: 8.0).isActive = true
I think we can agree that is neither pretty nor easy to understand (the Objective-C version is even worse). Using the Visual Format Language is not much better in my opinion:
let views: [String: AnyObject] =
["stackView" : stackView,
"topLayoutGuide" : topLayoutGuide]
let h = NSLayoutConstraint.constraints(
withVisualFormat: "|-[stackView]-|",
options: [],
metrics: nil,
views: views)
NSLayoutConstraint.activate(h)
let v = NSLayoutConstraint.constraints(
withVisualFormat: "V:|[topLayoutGuide]-[stackView]",
options: [],
metrics: nil,
views: views)
NSLayoutConstraint.activate(v)
Creating Constraints with Layout Anchors
Layout anchors make it much easier to create constraints. From the class documentation:
The NSLayoutAnchor class is a factory class for creating NSLayoutConstraint objects using a fluent API. Use these constraints to programatically define your layout using Auto Layout.
Layout anchors are properties on a UIView
(or UILayoutGuide
). Each property is a subclass of NSLayoutAnchor
with methods to directly create constraints to other layout anchors of the same type. A UIView has twelve different layout anchor properties you can use to create horizontal, vertical or size-based constraints:
Horizontal Constraints
Layout Anchors of type NSLayoutXAxisAnchor
for creating horizontal constraints:
centerXAnchor
leadingAnchor
andtrailingAnchor
leftAnchor
andrightAnchor
For example, to create a constraint to center align two views:
// Swift
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Objective-C
[self.myView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = YES;
Note how you start with an anchor on one view and create the constraint to an anchor on another view.
Vertical constraints
Layout Anchors of type NSLayoutYAxisAnchor
for creating vertical constraints:
centerYAnchor
bottomAnchor
andtopAnchor
firstBaselineAnchor
andlastBaselineAnchor
For example, to create a constraint between the top and bottom anchors of two views with a constant spacing:
// Swift
myView.bottomAnchor.constraint(equalTo: view.topAnchor,
constant: 8).isActive=true
// Objective-C
[self.myView.bottomAnchor constraintEqualToAnchor:self.view.topAnchor
constant:8.0].active = YES;
Size-Based Constraints
Layout Anchors of type NSLayoutDimension
for creating size-based constraints:
heightAnchor
andwidthAnchor
For example, to create a width constraint for a view:
// Swift
myView.widthAnchor.constraint(equalToConstant: 50.0).isActive = true
// Objective-C
[self.myView.widthAnchor constraintEqualToConstant:50.0].active = YES;
A further example, to make the height of a view twice the height of another view using a multiplier:
// Swift
myView.heightAnchor.constraint(equalTo: otherView.heightAnchor,
multiplier: 2.0).isActive = true
// Objective-C
[self.myView.heightAnchor constraintEqualToAnchor:self.otherView.heightAnchor
multiplier:2.0].active = YES;
View margins
A UIView
does not have layout anchors for the leading and trailing margins we used when creating our stack view constraints. Instead iOS 9 added two new properties, layoutMarginGuide
and readableContentGuide
, which in turn have layout anchors.
For example, to constrain the leading edge of a subview to the leading margin of the superview:
// Swift
let margins = view.layoutMarginsGuide
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
// Objective-C
UILayoutGuide *margins = self.view.layoutMarginsGuide;
[self.myView.leadingAnchor constraintEqualToAnchor:
margins.leadingAnchor].active = YES;
Top and Bottom Layout Guides
Note: The top and bottom layout guides were replaced by the Safe Area Layout Guide in iOS 11.
A view controller has both a topLayoutGuide
and bottomLayoutGuide
property for when you want to position content relative to top or bottom UIKit toolbars. Starting with iOS 9, both properties conform to the UILayoutSupport
protocol which gives us bottomAnchor
, topAnchor
and heightAnchor
properties for the bar.
For example, to position a view 8 points below the bottom of the top layout guide:
// Swift
myView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor,
constant: 8.0).isActive = true
// Objective-C
[self.stackView.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor
constant:8.0].active = YES;
Putting It All Together
So how would we create our stack view constraints using layout anchors?
First we get the leading and trailing margins of the superview:
let margins = view.layoutMarginsGuide
Then we create the leading and trailing horizontal constraints:
stackView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
Finally we use the topLayoutGuide property of the view controller to pin our stack view 8 points below the navigation bar:
stackView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor,
constant: 8.0).isActive = true
The Objective-C version is slightly more verbose but still a big improvement:
UILayoutGuide *margins = self.view.layoutMarginsGuide;
[self.stackView.leadingAnchor
constraintEqualToAnchor:margins.leadingAnchor].active = YES;
[self.stackView.trailingAnchor
constraintEqualToAnchor:margins.trailingAnchor].active = YES;
[self.stackView.topAnchor
constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor
constant:8.0].active = YES;
I find it much easier to understand the intent of these constraints compared to the earlier code.
Sample Code
You can find the Swift code snippets from this post in a sample Auto Layout Xcode project in my GitHub Code Examples repository.