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 UINavigationController, the .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:

How the topLayoutGuide and bottomLayoutGuide change position depending on the view controller they’re embedded in.

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 topLayoutGuide and 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. 😁

Essentially, 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 leadingAnchor and trailingAnchor — else you risk your app have the notch “cut out” of your UI.

Conclusion

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 edgesForExtendedLayout unless you absolutely must
  • Do constrain to the topLayoutGuide.bottomAnchor or bottomLayoutGuide.topAnchor
  • OR do constrain to the safeAreaLayoutGuide, if you want your app to look great on an iPhone X in landscape.

Enjoy your notches!