Goodbye Spacer Views Hello Layout GuidesFeb 29, 2016 · 5 minute read · Comments
Auto LayoutiOS 9
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.
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:
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:
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
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 // Objective-C UILayoutGuide *margins = self.view.layoutMarginsGuide;
Leading Layout Guide
The leading layout guide controls the space between the left margin and left button:
// Swift margins.leadingAnchor.constraintEqualToAnchor(leadingGuide.leadingAnchor).active = true leadingGuide.trailingAnchor.constraintEqualToAnchor(noButton.leadingAnchor).active = true // Objective-C [margins.leadingAnchor constraintEqualToAnchor:self.leadingGuide.leadingAnchor].active = YES; [self.leadingGuide.trailingAnchor constraintEqualToAnchor:self.noButton.leadingAnchor].active = YES;
Middle Layout Guide
The middle layout guide controls the space between the two buttons:
// Swift noButton.trailingAnchor.constraintEqualToAnchor(middleGuide.leadingAnchor).active = true middleGuide.trailingAnchor.constraintEqualToAnchor(yesButton.leadingAnchor).active = true // Objective-C [self.noButton.trailingAnchor constraintEqualToAnchor:self.middleGuide.leadingAnchor].active = YES; [self.middleGuide.trailingAnchor constraintEqualToAnchor:self.yesButton.leadingAnchor].active = YES;
Trailing Layout guide
The trailing layout guide controls the space between the right button and right margin:
// Swift yesButton.trailingAnchor.constraintEqualToAnchor(trailingGuide.leadingAnchor).active = true trailingGuide.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true // Objective-C [self.yesButton.trailingAnchor constraintEqualToAnchor:self.trailingGuide.leadingAnchor].active = YES; [self.trailingGuide.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
A constraint between the width anchors of the two buttons gives them equal width:
// Swift noButton.widthAnchor.constraintEqualToAnchor(yesButton.widthAnchor).active = true // Objective-C [self.noButton.widthAnchor constraintEqualToAnchor:self.yesButton.widthAnchor].active = YES;
We also want our three spacing guides to have equal width:
// Swift leadingGuide.widthAnchor.constraintEqualToAnchor(middleGuide.widthAnchor).active = true leadingGuide.widthAnchor.constraintEqualToAnchor(trailingGuide.widthAnchor).active = true // Objective-C [self.leadingGuide.widthAnchor constraintEqualToAnchor:self.middleGuide.widthAnchor].active = YES; [self.leadingGuide.widthAnchor constraintEqualToAnchor:self.trailingGuide.widthAnchor].active = YES;
Finally we set the vertical positions of everything using a center Y anchor constraint:
// Swift leadingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true middleGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true trailingGuide.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true noButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true yesButton.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true // Objective-C [self.leadingGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; [self.middleGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; [self.trailingGuide.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; [self.noButton.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES; [self.yesButton.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = YES;
You can find the full Swift code from this post in my sample Auto Layout Xcode project in my GitHub Code Examples repository.