A quick guide to laying out views in iOS
How to prevent tab bars and navigation bars getting in your way.
Recently, like many of you, we’ve been tackling some compatibility issues with the iPhone X. A lot of these boiled down to not using the APIs Apple gave us a few years ago, in order to make this easier, and not everyone was familiar with these, or how to use them.
So here’s a really quick guide!
Layout guides keep views from going underneath bars
View Controllers have two layout guides — the
topLayoutGuide and the
bottomLayoutGuide (yes, these were deprecated in iOS 11. You can read more about that nearer the bottom of this post).
They’re very useful — in the case your view controller is in a
.bottomAnchor property of the
topLayoutGuide corresponds to the bottom of the navigation bar. In the case that it isn’t, this bottom anchor is simply the top of the view controller.
What this means is that if you want a view to be pinned below the navigation bar, you should use something like this:
label.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
If you constrain this to the view controller’s
.view.topAnchor instead, you risk your view being underneath the navigation bar — probably not what you want. Also note that the top of the label is constrained to the bottom of the layout guide — a really frequent mistake is to do this the wrong way round!
Likewise, to avoid items going underneath the the tab bar, constrain items to the
bottomLayout.topAnchor, instead of the
view.bottomAnchor. Even if your views aren’t in a tab bar controller or navigation bar controller, its a good idea to use these anchors, in case that you change this in the future.
If you see any constraints to a view controller’s view’s top or bottom, you’re going to want to change them:
label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true // change me!!!
⚠️ A really annoying thing is that these layout guides are not available in XIBs, nor is it possible to initalise
NSLayoutConstraint to use a layout guide. They just don’t exist. If you want to use them (and you do!), you have to do them in code as above, or use storyboards. This is improved with the
safeAreaLayoutGuide, which we talk about below.
Here’s a good overview of the layout guides:
Use edgesForExtendedLayout if you are a rebel
A view controller also has a property
edgesForExtendedLayout, that you can use. This indicates which edges of a view can go under chrome provided by a parent view controller (e.g. the nav bar or tab bar). You can simply turn this off using
edgesForExtendedLayout = . However, then you don’t get the nice behaviour that the content slides under the semi-transparent navigation bar, and it can look weird.
Use at your peril.
Safe areas make your app fully iPhone X-proof
safeAreaLayoutGuide is the new and shiny API intended to replace
bottomLayoutGuide. Confusingly, a safe area layout guide is on a view and not a view controller, like
topLayoutGuide was. If you’re using a XIB or storyboard, you can choose to use this by checking “Safe Area Layout Guides” in the File Inspector.
In most cases, the
topAnchor of the
safeAreaLayoutGuide is equivalent to the
bottomAnchor of the old
topLayoutGuide. Confused? You’re not alone. 😁
top/bottomLayoutGuide defines the areas you shouldn’t put stuff, where as
safeAreaLayoutGuide defines the areas you should put stuff.
As safe areas are on views, this means that you can use them in XIBs, which is great. You still can’t use them using the NSLayoutConstraint initialiser, however. Safe areas also have the benefit of making the iPhone X in landscape look OK using the
trailingAnchor — else you risk your app have the notch “cut out” of your UI.
Take these things into account if you want your app to work for iPhone X:
- Definitely don’t constrain to a view controller’s view’s top or bottom
- Don’t fiddle with
edgesForExtendedLayoutunless you absolutely must
- Do constrain to the
- OR do constrain to the
safeAreaLayoutGuide, if you want your app to look great on an iPhone X in landscape.
Enjoy your notches!