SwiftUI のレイアウト優先度(layoutPriority)を使いこなす

SwiftUI のレイアウトシステムでは、スタック内の複数のビューが限られたスペースを奪い合う場面があります。layoutPriority は、どのビューがスペース配分で優先されるかを数値で指定するモディファイアです。デフォルトは 0 で、値が大きいほど優先的にスペースを割り当てられます。

スペースの奪い合い

まず、layoutPriority を使わない場合の挙動を確認しましょう。

HStack {
    Text("長いテキストが入ります。非常に長い説明文です。")
        .lineLimit(1)
        .background(Color.blue.opacity(0.2))
    
    Text("短いテキスト")
        .lineLimit(1)
        .background(Color.green.opacity(0.2))
}
.frame(width: 300)

HStack の幅が足りない場合、SwiftUI は同じ優先度のビューにスペースを等分に配分しようとします。結果として、両方のテキストが中途半端に省略されることがあります。

layoutPriority で優先順位を設定する

HStack {
    Text("長いテキストが入ります。非常に長い説明文です。")
        .lineLimit(1)
        .layoutPriority(1)
        .background(Color.blue.opacity(0.2))
    
    Text("短いテキスト")
        .lineLimit(1)
        .background(Color.green.opacity(0.2))
}
.frame(width: 300)

左の Text に .layoutPriority(1) を設定すると、まず左のテキストがスペースを優先的に確保し、残りが右のテキストに割り当てられます。右のテキストが先に省略されるようになるため、重要な情報を確実に表示したい場面で役立ちます。

スペース配分の仕組み

layoutPriority によるスペース配分は段階的に行われます。

最も優先度の高いビューに必要なスペースを割り当てる

残ったスペースを次に優先度の高いビューに割り当てる

最も優先度の低いビューに残りのスペースを配分する

この段階的な配分が行われるため、優先度の高いビューはほぼ理想のサイズを確保でき、低いビューが圧縮を引き受けることになります。

実践例:ナビゲーションバー風レイアウト

タイトル、サブタイトル、アクションボタンが横並びの UI で、タイトルを優先して表示したいケースを考えてみましょう。

HStack(spacing: 8) {
    VStack(alignment: .leading) {
        Text("プロジェクト設定")
            .font(.headline)
            .lineLimit(1)
        Text("詳細オプションを管理")
            .font(.caption)
            .foregroundColor(.secondary)
            .lineLimit(1)
    }
    .layoutPriority(1)
    
    Spacer()
    
    Button("保存") { }
        .buttonStyle(.borderedProminent)
}
.padding(.horizontal)

VStack に .layoutPriority(1) を付けることで、テキスト部分が優先的にスペースを確保します。画面が狭くなっても「保存」ボタンのサイズが先に確定した後にテキスト領域にスペースが配分されるため、ボタンが押しつぶされることがありません。

複数の優先度レベル

layoutPriority は Double 型なので、3 段階以上の優先順位も設定できます。

HStack {
    Text("高")
        .layoutPriority(2)
        .background(Color.red.opacity(0.2))
    
    Text("中")
        .layoutPriority(1)
        .background(Color.yellow.opacity(0.2))
    
    Text("低(デフォルト)")
        .background(Color.gray.opacity(0.2))
}

優先度 2 → 1 → 0(デフォルト)の順にスペースが配分されます。ただし、3 段階以上の優先度を使う場面はあまり多くありません。ほとんどのケースでは、特に重要なビューだけに .layoutPriority(1) を付ける 2 段階の運用で十分でしょう。

layoutPriority を使わない場合

すべてのビューが同じ優先度(0)で、スペースが均等に配分される。どのビューが省略されるか予測しにくい

layoutPriority を使う場合

重要なビューに高い値を設定することで、スペース不足時に何が省略されるかを明示的にコントロールできる

fixedSize との違い

layoutPriority と fixedSize はどちらもスペース配分に関わりますが、アプローチが異なります。layoutPriority は「他のビューとの相対的な優先順位」を設定するのに対し、fixedSize は「親のサイズ提案を完全に無視して理想サイズを取る」という絶対的な指定です。

layoutPriority は親の提案を尊重しつつ配分の優先順位を変えるだけなので、レイアウトが予測しやすく安全に使えます。はみ出しのリスクがある fixedSize よりも、まず layoutPriority で解決できないかを検討するのがよいでしょう。