Goodbye Spacer Views Hello Layout Guides

The use of hidden spacer or dummy views is a common technique with auto layout when you need to set constraints on a rectangular space in the view. They allow you to control the spacing between views or to group related objects.

The disadvantage is that they are still real views in the view hierarchy consuming memory and able to respond to messages in the responder chain. Apple added the UILayoutGuide class in iOS 9 to do the same job as spacer or dummy views without the overhead.

Last updated: Jun 12, 2020

Equally Spaced Buttons

Let’s look at a common use for spacer views. Suppose we have two buttons that we want to be evenly spaced horizontally:

Equally spaced buttons

The two buttons have a fixed width set to fit the longest title. The spacing is evenly distributed between each button and the margins of the superview. The width of these spaces changes as the superview changes (e.g. on rotation) but are always equal to each other.

Since you cannot set constraints on empty space it is common to add dummy spacer views where we want the space. Here is the same screenshot with the space between the buttons and margins made visible so you can see what is going on:

Layout Guides

If you are supporting iOS 8 or earlier the easiest way to create this layout is to add three hidden UIView objects to the view hierarchy to act as the yellow spaces. With iOS 9 you can replace these views with three layout guides. A UILayoutGuide has a layout frame (CGRect), layout anchors and an owning view but it is not part of the view hierarchy.

Note: Interface Builder does not support layout guides yet (Xcode 7.2.1). So if you want to use them you must create your constraints in code.

Setting up the Views and Layout Guides

To create this layout we first add the three layout guides and the two buttons as properties of the view controller. I will show each code snippet first in Swift and then in Objective-C:

let leadingGuide = UILayoutGuide()
let noButton = UIButton(type: .Custom)
let middleGuide = UILayoutGuide()
let yesButton = UIButton(type: .Custom)
let trailingGuide = UILayoutGuide()

The Objective-C version of this setup code is more verbose, we first need some properties for each view and guide:

@interface ViewController ()
@property (nonatomic, strong) UILayoutGuide *leadingGuide;
@property (nonatomic, strong) UILayoutGuide *middleGuide;
@property (nonatomic, strong) UILayoutGuide *trailingGuide;
@property (nonatomic, strong) UIButton *noButton;
@property (nonatomic, strong) UIButton *yesButton;
@end

Then in the view setup code we create each object:

self.noButton = [UIButton new];
self.yesButton = [UIButton new];
self.leadingGuide = [UILayoutGuide new];
self.middleGuide = [UILayoutGuide new];
self.trailingGuide = [UILayoutGuide new];

I am going to skip some of the boilerplate code to configure the buttons. The code is all called from the viewDidLoad method of the view controller. See the example project for details. Once we have the buttons we add them as subviews to the top level view as normal:

// Swift
view.addSubview(noButton)
view.addSubview(yesButton)  
view.addLayoutGuide(leadingGuide)
view.addLayoutGuide(middleGuide)
view.addLayoutGuide(trailingGuide)

Note that we do not use addSubview for the layout guides. Remember that layout guides are not part of the view hierarchy. A layout guide needs an owning view which you assign with the addLayoutGuide method.

The Objective-C code is similar:

// Objective-C
[self.view addSubview:self.noButton];
[self.view addSubview:self.yesButton];
[self.view addLayoutGuide:self.leadingGuide];
[self.view addLayoutGuide:self.middleGuide];
[self.view addLayoutGuide:self.trailingGuide];

Adding the Constraints

With the views and layout guides created we can add our constraints. This is pretty much the same as if we were using spacing views. First we will set the leading and trailing constraints working from left to right. A layout guide has layout anchors just like a regular view that we use to set the constraints. If you need a recap on how to use layout anchors see my post on pain free constraints with layout anchors.

Layout Margin Guides

We are spacing the buttons relative to the margins of the superview so we will need the layout margins guide:

// Swift
let margins = view.layoutMarginsGuide
NSLayoutConstraints.activate([
// Objective-C
UILayoutGuide *margins = self.view.layoutMarginsGuide;
[NSLayoutConstraint activateConstraints:@[

Leading Layout Guide

The leading layout guide controls the space between the left margin and left button:

// Swift
margins.leadingAnchor.constraintEqualToAnchor(leadingGuide.leadingAnchor),
leadingGuide.trailingAnchor.constraintEqualToAnchor(noButton.leadingAnchor)
// Objective-C
[margins.leadingAnchor constraintEqualToAnchor:self.leadingGuide.leadingAnchor],
[self.leadingGuide.trailingAnchor constraintEqualToAnchor:self.noButton.leadingAnchor],

Middle Layout Guide

The middle layout guide controls the space between the two buttons:

// Swift
noButton.trailingAnchor.constraintEqualToAnchor(middleGuide.leadingAnchor)
middleGuide.trailingAnchor.constraintEqualToAnchor(yesButton.leadingAnchor)
// Objective-C
[self.noButton.trailingAnchor constraintEqualToAnchor:self.middleGuide.leadingAnchor],
[self.middleGuide.trailingAnchor constraintEqualToAnchor:self.yesButton.leadingAnchor],

Trailing Layout guide

The trailing layout guide controls the space between the right button and right margin:

// Swift
yesButton.trailingAnchor.constraintEqualToAnchor(trailingGuide.leadingAnchor),
trailingGuide.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor),
// Objective-C
[self.yesButton.trailingAnchor constraintEqualToAnchor:self.trailingGuide.leadingAnchor],
[self.trailingGuide.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor],

Equal Widths

A constraint between the width anchors of the two buttons gives them equal width:

// Swift
noButton.widthAnchor.constraintEqualToAnchor(yesButton.widthAnchor),
// Objective-C
[self.noButton.widthAnchor constraintEqualToAnchor:self.yesButton.widthAnchor],

We also want our three spacing guides to have equal width:

// Swift
leadingGuide.widthAnchor.constraintEqualToAnchor(middleGuide.widthAnchor),
leadingGuide.widthAnchor.constraintEqualToAnchor(trailingGuide.widthAnchor),
// Objective-C
[self.leadingGuide.widthAnchor constraintEqualToAnchor:self.middleGuide.widthAnchor],
[self.leadingGuide.widthAnchor constraintEqualToAnchor:self.trailingGuide.widthAnchor],

Vertical Postion

Finally we set the vertical positions of everything using a center Y anchor constraint:

// Swift
leadingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
middleGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
trailingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
noButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
yesButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor)
])
// Objective-C
[self.leadingGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.middleGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.trailingGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.noButton.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.yesButton.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]
]];

Sample Code

You can find the full Swift code from this post in my sample Auto Layout Xcode project in my GitHub Code Examples repository.

Further Reading