SwiftUI の overlay・background の配置ルールを理解する

overlay と background は、ビューの前面や背面に別のビューを重ねるモディファイアです。ZStack と似た効果を得られますが、「基準となるビューのサイズに従属する」という重要な違いがあります。この配置ルールを理解することで、装飾やバッジの重ね合わせをより的確に制御できるようになります。

background の基本

background はビューの背後に別のビューを配置します。

Text("SwiftUI")
    .font(.title)
    .padding()
    .background(
        RoundedRectangle(cornerRadius: 12)
            .fill(Color.blue.opacity(0.2))
    )

Text のサイズ(padding を含む)が基準となり、RoundedRectangle はそのサイズに合わせて配置されます。背景のビューが基準ビューより大きくても小さくても、基準ビューのサイズに影響はありません。

overlay の基本

overlay はビューの前面に別のビューを配置します。

Image(systemName: "person.circle.fill")
    .font(.system(size: 60))
    .overlay(alignment: .bottomTrailing) {
        Circle()
            .fill(Color.green)
            .frame(width: 16, height: 16)
            .overlay(
                Circle().stroke(Color.white, lineWidth: 2)
            )
    }

プロフィール画像の右下にオンライン状態を示す緑の丸を重ねるパターンです。overlay の alignment パラメータで配置位置を指定できます。

ZStack との違い

overlay / background と ZStack は見た目が似ていますが、サイズの決まり方が異なります。

ZStack

すべての子ビューの中で最も大きいものがコンテナのサイズを決める。どの子ビューが最大になるかで全体サイズが変動する

overlay / background

モディファイアを付けたビュー(基準ビュー)がサイズを決める。重ねたビューはサイズに影響しない

この違いは実用上とても重要です。たとえば、テキストの上にバッジを重ねるとき、ZStack ではバッジが大きいとコンテナ全体が膨らんでしまいます。overlay を使えば、テキストのサイズだけが基準になるため、バッジがはみ出してもレイアウトが崩れません。

alignment パラメータ

overlay と background はどちらも alignment パラメータを受け取ります。9 方向の配置が可能で、デフォルトは .center です。

Text("通知")
    .padding(.horizontal, 12)
    .padding(.vertical, 8)
    .background(Color.gray.opacity(0.2))
    .cornerRadius(8)
    .overlay(alignment: .topTrailing) {
        Text("5")
            .font(.caption2)
            .foregroundColor(.white)
            .padding(4)
            .background(Color.red)
            .clipShape(Circle())
            .offset(x: 8, y: -8)
    }

.topTrailing で右上に配置し、offset で微調整しています。バッジを基準ビューの外側にはみ出させたい場合は、このように offset を併用するのが定番です。

複数の overlay / background を重ねる

overlay と background はそれぞれ複数回適用できます。モディファイアチェーンの順序が重ね順を決めます。

Text("Hello")
    .padding()
    .background(Color.white)
    .background(
        RoundedRectangle(cornerRadius: 12)
            .fill(Color.blue)
            .padding(-4)
    )
    .overlay(alignment: .topLeading) {
        Image(systemName: "star.fill")
            .foregroundColor(.yellow)
            .offset(x: -8, y: -8)
    }
    .overlay(alignment: .bottomTrailing) {
        Text("NEW")
            .font(.caption2)
            .padding(4)
            .background(Color.red)
            .foregroundColor(.white)
            .cornerRadius(4)
            .offset(x: 4, y: 4)
    }

背景に白→青枠、前面に星アイコンと NEW バッジという多層構造です。各 overlay / background が独立しているため、装飾を段階的に追加・削除しやすいのがメリットになります。

iOS 15 以前と iOS 16 以降の構文

iOS 15 までの background と overlay はクロージャ構文を取りませんでした。iOS 16 以降ではトレーリングクロージャ構文が使えるようになり、alignment 指定もシンプルになっています。

// iOS 15 まで
.overlay(
    Circle().fill(Color.red),
    alignment: .topTrailing
)

// iOS 16 以降
.overlay(alignment: .topTrailing) {
    Circle().fill(Color.red)
}

iOS 16 以降のクロージャ構文の方が読みやすく、複雑なビューを渡すときも自然に書けます。ターゲットバージョンに応じて使い分けましょう。