SwiftUI の safeAreaInset・ignoresSafeArea でセーフエリアを制御する

iOS アプリではセーフエリア(ノッチやホームインジケーターを避けた安全な領域)を意識したレイアウトが不可欠です。SwiftUI はデフォルトでセーフエリア内にコンテンツを配置しますが、ignoresSafeArea で意図的にはみ出させたり、safeAreaInset で追加の余白を設けたりすることで、より柔軟なレイアウトを実現できます。

セーフエリアの基本動作

SwiftUI のビューはデフォルトでセーフエリア内に収まります。

VStack {
    Text("上端")
    Spacer()
    Text("下端")
}

このコードでは「上端」はステータスバーの下に、「下端」はホームインジケーターの上に配置されます。開発者が特別な指定をしなくても安全な位置にコンテンツが表示される仕組みです。

ignoresSafeArea でセーフエリアを無視する

背景色や画像を画面端まで広げたいときには ignoresSafeArea を使います。

ZStack {
    Color.blue
        .ignoresSafeArea()
    
    VStack {
        Text("コンテンツは安全な位置に")
            .foregroundColor(.white)
        Spacer()
    }
}

Color にだけ ignoresSafeArea を付け、テキストを含む VStack には付けていません。こうすることで、背景色は画面全体に広がりつつ、コンテンツはセーフエリア内に留まります。

edges と regions パラメータ

ignoresSafeArea は、無視するセーフエリアの辺と種類を細かく指定できます。

// 下端だけセーフエリアを無視
ScrollView {
    content
}
.ignoresSafeArea(edges: .bottom)

// キーボード領域のみ無視
TextField("入力", text: $text)
    .ignoresSafeArea(.keyboard, edges: .bottom)
edges パラメータ

.top、.bottom、.leading、.trailing、.all などを指定し、どの辺のセーフエリアを無視するかを選べる。デフォルトは .all になっている。

regions パラメータ

.container(ノッチやホームインジケーター)と .keyboard(キーボード)を区別できる。キーボード表示時のレイアウト制御に使うことが多い。

safeAreaInset でカスタム余白を追加する

iOS 15 で導入された safeAreaInset は、セーフエリアに追加のスペースを挿入するモディファイアです。フローティングボタンやカスタムツールバーの領域を確保するのに最適です。

ScrollView {
    ForEach(0..<30) { index in
        Text("アイテム \(index)")
            .padding()
            .frame(maxWidth: .infinity, alignment: .leading)
    }
}
.safeAreaInset(edge: .bottom) {
    HStack {
        Button("キャンセル") { }
            .buttonStyle(.bordered)
        Spacer()
        Button("保存") { }
            .buttonStyle(.borderedProminent)
    }
    .padding()
    .background(.ultraThinMaterial)
}

safeAreaInset で下端に固定バーを配置すると、ScrollView のコンテンツはそのバーの分だけ余白を確保します。最後のアイテムまでスクロールしてもバーに隠れることがありません。

overlay との違い

フローティングボタンの実装で overlay を使う方法もありますが、safeAreaInset とは挙動が異なります。

overlay

ビューの上に重ねるだけで、スクロールコンテンツの余白は調整されない。最後のアイテムがバーに隠れてしまう

safeAreaInset

セーフエリアを拡張するため、スクロールコンテンツが自動的にバーの高さ分だけ余白を確保する

safeAreaInset は単に「ビューを配置する」だけでなく「セーフエリア自体を変更する」ため、ScrollView やリストと組み合わせたときに正しい余白が保たれます。

複数の safeAreaInset を組み合わせる

safeAreaInset は複数回適用できます。上端と下端それぞれにバーを追加することも可能です。

List(items) { item in
    ItemRow(item: item)
}
.safeAreaInset(edge: .top) {
    SearchBar(text: $searchText)
        .padding(.horizontal)
        .background(.bar)
}
.safeAreaInset(edge: .bottom) {
    BottomToolbar()
        .background(.bar)
}

上端に検索バー、下端にツールバーを配置しつつ、リストのスクロール領域はそれぞれの高さを自動的に避けてくれます。カスタム UI を構築するときの強力な選択肢として覚えておくとよいでしょう。