SwiftUI Field Guide

The Safe Area

The safe area is the part of the screen that is visible to the user without being obstructed by system-provided controls such as the status bar, navigation bar, home indicator or tab bars. In the example below, the safe area is marked in green.

iPhone 15

In the example above, we can see that the scroll view becomes exactly as large as the safe area, yet its content is still visible outside of the safe area. Put another way, we could say that the scroll view in the example above is exactly as large as the screen, but has its content inset to the size of the safe area. To the outside, however, the bounds of the scroll view are the bounds of the safe area. This way, when the user scrolls all the way to the top or bottom, the content is not obstructed.

When we select the second tab, we can see that the resizable image fills the safe area exactly, but does not extend beyond the safe area. Most views in SwiftUI behave like this, but we can also control this ourselves. In the third tab, we used a grid but made it ignore the safe area. It stretches behind the navigation bar and toolbar, all the way to the edges of the screen.

Ignoring the Safe Area

In the example below, we have added two fake toolbars to the top and bottom of the screen using the safeAreaInset modifier. When we add the ignoresSafeArea modifier to our color, the view stretches beyond the safe area.

Code
Color.orange    .padding(.horizontal)    .ignoresSafeArea()    .safeAreaInset(edge: .top) { MyToolbar() }    .safeAreaInset(edge: .bottom) { MyToolbar() }
Preview

For more complicated hierarchies, it matters a lot where we apply the ignoresSafeArea modifier. In the example below, we have a vertical stack with two background colors. When we apply the modifier to the VStack, the entire stack stretches beyond the safe area. When we apply the modifier to the individual views, only the individual views stretch beyond the safe area. To see the difference, try enabling only the top bar or only the bottom bar and then toggling the placement of the modifier. We'll see that the stack still distributes the space evenly, but the individual views can then stretch beyond the safe area.

Code
VStack {    Color.yellow        /* .ignoresSafeArea() */    Color.orange        /* .ignoresSafeArea() */}.padding(.horizontal).ignoresSafeArea().safeAreaInset(edge: .top, spacing: 0) {    MyToolbar()}.safeAreaInset(edge: .bottom, spacing: 0) {    MyToolbar()}
Preview

Custom Insets

As we have seen above, we can add to the safe area using the safeAreaInset modifier. (As of iOS 17, we can also use safeAreaPadding to extend the safe area without providing a view). The safe area insets work like a stack: when we have an existing inset, additional modifiers append their size to the inset. In the example below, we can add multiple bottom bars to the screen, and the bottom safe area inset is their combined height.

Code
Color.orange    .padding(.horizontal)    .ignoresSafeArea()    .safeAreaInset(edge: .bottom, spacing: 0) {        MyToolbar()    }    .safeAreaInset(edge: .bottom, spacing: 0) {        MyToolbar()    }
Preview

Note that we might also want to use ignoresSafeAreawithin safeAreaInset views. For example, to show the bottom cart button behind the home indicator, we need to addignoresSafeArea to exactly the right view. When we add it to the cart the text will be obscured, but if we add it to the background, we get the effect we want.

Code
ProductPlaceholder()    .safeAreaInset(edge: .top, spacing: 0) {        MyToolbar()    }    .safeAreaInset(edge: .bottom, spacing: 0) {        HStack(alignment: .firstTextBaseline) {            ZStack {                Circle()                    .frame(width: 18, height: 18)                Text("1")                    .foregroundStyle(.accentColor)            }            Text("My Order")            Spacer()            Text("$19")        }        .foregroundStyle(.white)        .padding(8)        .frame(maxWidth: .infinity)        .background {            Color.accentColor                .ignoresSafeArea()        }        /* .ignoresSafeArea() */    }
Preview

Gotchas

When you use the background modifier, you might get the unexpected effect of a view stretching beyond the safe area. This happens because there are multiple variants of the background modifier. When we use the background modifier without a trailing closure and with no other parameters, the background(_:ignoresSafeAreaEdges:) variant is used, which takes a ShapeStyle instead of a View and has a default value of all for the edges. The Swift compiler chooses this overload because the type we provide (Color) is a more specific match than the other overloads. We can see the difference in the following example:

Code
Text("Hello, world.")    .font(.title)    .foregroundStyle(.white)    .frame(maxWidth: .infinity,           maxHeight: .infinity)    .background(Color.purple)    /* .background { Color.purple } */
Preview
Inline ModifierTrailing Closure

Geometry Reader

When you need programmatic access to the values of the safe area, you can use a geometry reader. In the example below, we visualized the size of the geometry reader using a blue background. When the geometry reader is not ignoring the safe area, the proxy's safeAreaInsets property contains non-zero values. When you ignore the safe area, the geometry reader fills the entire screen but the insets will be zero.

Code
GeometryReader { proxy in    VStack {        Text(            "\(proxy.size.width) x \(proxy.size.height)"        )        Text(            "top: \(proxy.safeAreaInsets.top), bottom: \(proxy.safeAreaInsets.bottom)"        )    }    .frame(width: proxy.size.width,           height: proxy.size.height)    .font(.caption)}.foregroundStyle(.white).background { Color.accentColor }/* .ignoresSafeArea() */.safeAreaInset(edge: .top, spacing: 0) {    MyToolbar()}.safeAreaInset(edge: .bottom, spacing: 0) {    MyToolbar()}
Preview

You can also use a geometry reader to provide a visualization of the safe area in your own code.

More Resources

Share Image

In this article, Fatbobman shows a number of both simple and advanced examples of how to work with the safe area in SwiftUI.

https://fatbobman.com/en/posts/safearea/
Share Image

In SwiftUI's official documentation we can see how to add a background to a view, and how to use the safe area insets to make sure the background is visible even when the keyboard shows.

https://developer.apple.com/documentation/swiftui/adding-a-background-to-your-view
Share Image

The Human Interface Guidelines contains an article about layout that talks about the safe area from a design perspective.

https://developer.apple.com/design/human-interface-guidelines/layout