[SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビューの調停)

SwiftUI2021

SwiftUI で コンテナを使った時のレイアウトの仕組みを説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterey 12.1 RC
  • Xcode 13.2 RC
  • iOS 15.2

push-out ビューのレイアウト

前回、push-out/pull-in タイプのビューを説明しました。
SwiftUI2021[SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビュー, pull-in タイプビュー とレイアウトの調整)

pull-in タイプのビューは、自分が必要とする領域を使用し、push-out タイプのビューは、可能な限り広い領域を使おうとします。

ある意味、pull-in タイプのビューの振る舞いはわかりやすいのですが、複数の push-out タイプのビューが領域を取り合うとどうなるのでしょうか?

確認してみます。

2つの push-out タイプのビューの調停

2つの push-out タイプのビューを並べた時の挙動を確認します。

単純にするために、VStack に 2つの Color を配置しています。

2Colors


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/12/09
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing:0) {
            Color.red
            Color.yellow
        }
    }
}

どのように調停されているのか

みたところ、使える領域の半分づつをそれぞれに与えられているように見えます。

状況を再確認してみると、Color.red と Color.yellow どちらも frame で固定高さを与えられておらず、さらにどちらも push-out タイプのビューです。

このような時には、VStack として それぞれに使える領域の半分を与えているように見えます。

# 公式にどのように Layout されるかの資料はありませんので、推測で書いてます。

この推測を補強するために、もう少し挙動を調べてみます。

3つの push-out タイプのビューの調停

予想通りです👍

数値でも確認できるようにするために、与えられている領域を数値で表示するようにしています。

3Colors


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/12/09
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing:0) {
            Color.red
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Color.yellow
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Color.green
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
        }
    }
}

3つの push-out タイプのビュー(Color.red, Color.yellow, Color.green) に均等に領域を与えていることが数値でも確認できました。

以前、Spacer の使い方の記事を書きましたが、あれは Spacer が push-out タイプのビューであることで、上記の性質を利用しているということがわかります。

SwiftUI[SwiftUI] あまり知られていない Spacer の使い方

pull-in タイプのビューが混ざって、複数の push-out タイプのビューがあるとき

複数の push-out タイプがあるときは、均等に領域が与えられることがわかりましたが、pull-in タイプのビューが混ざっている時には、どうなるか確認してみます。

結果として表示されている数値を見ると、Text に与えた分の高さが pull-out タイプのビューの高さから 均等に減っていることがわかります。(271.333 - (30 * 2)/3 = 251.333)

このことから、レイアウトする時には、最初に 指定値を持つ要素に領域を与え、指定値を与えられていない push-out タイプのビューは、残りを均等に分けて与えられると推測できます。

3ColorsWithText


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/12/09
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing:0) {
            Color.red
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Text("Hello").frame(height: 30).frame(maxWidth: .infinity)
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Color.yellow
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Text("world").frame(height: 30).frame(maxWidth: .infinity)
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
            Color.green
                .overlay{
                    GeometryReader { geom in
                        Text("\(geom.size.width) - \(geom.size.height)")
                    }
                }
        }
    }
}

わかったこと/わかっていないこと

  • わかったこと1:複数の push-out タイプビューは、領域を均等に与えられてレイアウトされる
  • わかったこと2:pull-in タイプビューは、必要とする領域を最初(push-out タイプビューよりも先)に与えられる
  • わかっていないこと1:領域が不足している時どうなる?
  • わかっていないこと2:ビューが階層化している時どうなる?

結構、複雑化しそうなので、今後も 少しづつ 確認・説明していきます。

まとめ: SwiftUI の layout システムを理解する (push-out タイプビューの調停)

SwiftUI の layout システムを理解する (push-out タイプビューの調停)
  • push-out タイプのビューは、残っている領域を使って表示しようとする
  • 複数の push-out タイプのビューは、残り領域を均等に与えられてレイアウトされる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です