SwiftUI の padding でコンテンツに余白をつける

padding はビューの周囲に余白を追加するモディファイアです。UIKit の layoutMargins に相当しますが、SwiftUI では宣言的にどこにでも挿入でき、適用順序によって結果が変わるという特徴を持っています。

基本の padding

引数なしで .padding() を付けると、上下左右にシステムデフォルトの余白が追加されます。

Text("Hello, SwiftUI!")
    .background(Color.yellow)
    .padding()
    .background(Color.blue.opacity(0.2))

2 つの background を付けることで、テキスト自体の領域(黄色)と padding を含む領域(青)の違いが視覚的にわかります。デフォルトの余白はプラットフォームによって異なりますが、iOS では約 16pt 前後が適用されます。

特定の辺だけに余白をつける

Edge.Set を使って、余白を付ける辺を限定できます。

Text("上だけ余白")
    .padding(.top, 20)

Text("水平方向に余白")
    .padding(.horizontal, 16)

Text("下と右に余白")
    .padding([.bottom, .trailing], 12)

.horizontal は左右、.vertical は上下のショートカットです。複数の辺を指定するときは配列で渡します。辺を指定しつつ値を省略すると、デフォルト値がその辺だけに適用されます。

padding の値を明示的に指定する

EdgeInsets を使うと、4 辺それぞれに異なる値を設定できます。

Text("個別指定")
    .padding(EdgeInsets(
        top: 8,
        leading: 16,
        bottom: 8,
        trailing: 16
    ))
    .background(Color.gray.opacity(0.2))
    .cornerRadius(8)

ボタンやカード風のコンポーネントで、水平方向と垂直方向で異なる余白を設定したいときに使います。

padding の適用順序がレイアウトを変える

SwiftUI のモディファイアはビューを外側からラップするため、padding と他のモディファイアの順番によって見た目が大きく変わります。

// パターンA: background → padding
Text("A")
    .background(Color.red.opacity(0.3))
    .padding()

// パターンB: padding → background
Text("B")
    .padding()
    .background(Color.red.opacity(0.3))

パターン A では背景色がテキストだけにつき、その外側に透明な余白がつきます。パターン B では余白を含む領域全体に背景色がつきます。カードやバッジの背景をつけたい場合はパターン B を使うのが一般的です。

background → padding(パターン A)

背景色はコンテンツにだけ適用され、padding 部分は透明のまま残る

padding → background(パターン B)

padding を含む領域全体に背景色が適用される。カード UI の定番パターン

padding を重ねがけする

padding は複数回適用することもできます。装飾の層を重ねたいときに有効なテクニックです。

Text("多層 padding")
    .padding(8)
    .background(Color.white)
    .cornerRadius(8)
    .padding(4)
    .background(Color.blue)
    .cornerRadius(12)
    .padding(4)
    .background(Color.gray.opacity(0.3))
    .cornerRadius(16)

内側から順に白背景→青いボーダー→グレーの外枠と、3 層の装飾が作れます。SwiftUI のモディファイアチェーンが「ビューの入れ子」であるという概念を理解すると、この書き方が自然に感じられるようになるでしょう。

負の padding

padding には負の値も指定できます。

HStack(spacing: 0) {
    Circle()
        .frame(width: 40, height: 40)
    Circle()
        .frame(width: 40, height: 40)
        .padding(.leading, -10)
}

2 つの円が 10pt 重なって表示されます。アバターを重ねて並べるようなデザインで使えるテクニックですが、多用するとレイアウトが読みにくくなるため、限定的に使うのがよいでしょう。