Apple introduced the topLayoutGuide
and bottomLayoutGuide
as properties of UIViewController
way back in iOS 7. They allowed you to create constraints to keep your content from being hidden by UIKit bars like the status, navigation or tab bar. These layout guides are deprecated in iOS 11 and replaced by a single safe area layout guide.
Top and Bottom Layouts Guides - A Recap
An example of using the top and bottom layout guides with a view controller embedded in both a navigation controller and tab bar:
The green content view has a top constraint to the bottom anchor of the top layout guide and a bottom constraint to the top anchor of the bottom layout guide. The layout guides adapt to the presence of and size of the various bars.
Safe Area Layout Guide
New in iOS 11, Apple is deprecating the top and bottom layout guides and replacing them with a single safe area layout guide:
Our constraints are now with the top and bottom anchors of the safe area layout guide. This is not a big change but I think it is simpler to understand. You also have convenient access to layout anchors for the width, height and centers of the safe area.
Migrating Storyboards
Apple has tried to automate the change in Xcode 9 if you create the constraints in a Storyboard. Here are my constraints created in Xcode 8 for the earlier example:
The constraints to the top and bottom layout guides have a standard spacing constant to create some padding:
I don’t know if this will change in the future but at the time of writing with Xcode 9 beta 2 you do not get a deprecation warning until you change your deployment target to iOS 11.0.
Apple told us in WWDC 2017 Session 412 that Storyboards using safe areas are backwards deployable. This means you can switch to using the safe area layout guide in Interface Builder even if you still target iOS 10 and older.
You convert from top and bottom layout guides to the safe area layout guide by changing the setting in the file inspector for the Storyboard. You need to do this for each Storyboard in your project.
The Storyboard automatically replaces the top and bottom layout guides with a safe area and updates the constraints:
Missing Spacing
Unfortunately the migration is not without problems. Constraints to the safe area layout guide do not seem to cope with standard spacing. Inspecting the constraint shows it as having a standard spacing:
But the space is missing when the layout runs:
This seems like a bug (rdar://32970194) in Interface Builder as it does not allow you to create constraints to the safe area layout guide with “standard” spacing. The workaround is to manually add back the missing spacing to the top and bottom constraints:
Hopefully Apple fixes this in a future update as manually fixing this for larger projects will be painful.
Creating Constraints in Code
If you create your constraints in code use the safeAreaLayoutGuide
property of UIView
to get the relevant layout anchors. Let’s recreate the above Interface Builder example in code to see how it looks:
Assuming we have the green view as a property in our view controller:
private let greenView = UIView()
We might have a function to set up the views and constraints called from viewDidLoad
:
private func setupView() {
greenView.translatesAutoresizingMaskIntoConstraints = false
greenView.backgroundColor = .green
view.addSubview(greenView)
Create the leading and trailing margin constraints as always using the layoutMarginsGuide
of the root view:
let margins = view.layoutMarginsGuide
NSLayoutConstraint.activate([
greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
])
Now unless you are targeting iOS 11 only you will need to wrap the safe area layout guide constraints with #available and fall back to top and bottom layout guides for earlier iOS versions:
if #available(iOS 11, *) {
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
greenView.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
guide.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1.0)
])
} else {
let standardSpacing: CGFloat = 8.0
NSLayoutConstraint.activate([
greenView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
bottomLayoutGuide.topAnchor.constraint(equalTo: greenView.bottomAnchor, constant: standardSpacing)
])
}
Notes:
- The
safeAreaLayoutGuide
is a property ofUIView
wheretopLayoutGuide
andbottomLayoutGuide
are properties ofUIViewController
. The beta seeds of iOS 11 also have asafeAreaLayoutGuide
property onUIViewController
but this is deprecated. - The
constraintEqualToSystemSpacingBelow
method is new in iOS 11 and removes the need to hard code standard spacing. There are also less than and greater than versions. For horizontal spacing there is alsoconstraintEqualToSystemSpacingAfter
. - If you have a custom toolbar you can increase the size of the safe area with the
additionalSafeAreaInsets
property onUIViewController
.