[SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビュー, pull-in タイプビュー とレイアウトの調整)

SwiftUI2021

SwiftUI での レイアウトを理解するのに必要な Push-out/Pull-in タイプビュー を説明します

環境&対象

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

  • macOS Monterey 12.1 beta3
  • Xcode 13.2 beta2
  • iOS 15

push-out/pull-in タイプのビュー

SwiftUI のドキュメントには明確に記載されていませんが、SwiftUI には、(少なくとも)2つのタイプのビューがあります。

  • push-out タイプ
  • pull-in タイプ

まずは、それぞれを説明します。

以下では、iPhone11 のシミュレータで動作させているので、414 x 814 のサイズでのスクリーンショットです。

push-out タイプのビュー

例えば、Color は、SwiftUI ではそれだけで View になる push-out タイプのビューです。

.frame 指定をせずに使う

Color は、使用できる最大領域を使用して表示されます。

ColorView


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        Color.yellow
    }
}

.frame 指定してみる

.frame で領域指定すると、Color は、指定された領域の最大値を取得・利用しようとします。

ColorViewWithFrame


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        Color.yellow
            .frame(width: 200, height: 100)
    }
}
MEMO
Color 視点では、上位から与えられた領域全てを使用している点では同じ挙動をしています。

.frame については、以下の記事で説明しています。
SwiftUI2021[SwiftUI] SwiftUI の layout システムを理解する ( .frame の働き)

このように、与えられた領域すべてを使おうとするビューを push-out タイプのビューと呼びます。

pull-in タイプのビュー

Text のような 自分が必要とする領域のみを使用するビューを pull-in タイプのビューと呼びます。

.frame 指定をしていない

自分の必要とする領域だけを使っていることが、background 表示からわかります。

TextWithoutFrame


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello world")
            .background(Color.yellow)
    }
}

.frame 指定してみる

.frame を指定しても、Text の使用領域は変わらないことがわかります。

TextWithFrame


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello world")
            .background(Color.yellow)
            .frame(width: 200, height: 100)
            .background(Color.green)
    }
}

一方向にのみ push-out タイプのビューも存在する

Color のように、縦横両方に広がるタイプとは異なり、一方向にのみ広がるタイプも存在します。

例えば、TextField は、横方向に push-out タイプであり、縦方向には、pull-in タイプです。

TextField


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

import SwiftUI

struct ContentView: View {
    @State private var text = "Hello"

    var body: some View {
        TextField("text field", text: $text)
            .background(Color.yellow)
    }
}

.frame でサイズ指定してみると、横方向には push-out であり、縦方向には pull-in であることがよくわかります。

TextFieldWithFrame


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

import SwiftUI

struct ContentView: View {
    @State private var text = "Hello"

    var body: some View {
        TextField("text field", text: $text)
            .background(Color.yellow)
            .frame(width: 200, height: 200)
            .background(Color.green)
    }
}

push-out タイプと pull-in タイプが混在するとどうなるか?

push-out タイプのビューと pull-in タイプのビューが混在するとどうなるか確認してみます。

push-out と pull-in の共存

サイズ的に共存できるケースを確認します。

ColorAndText


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing:0){
            Text("Hello world")
                .background(Color.green)
            Color.yellow
        }
    }
}

Text で確保された領域以外は、Color が使っていることがわかります。(SafeArea 周りの挙動は特殊です)

pull-in タイプは、自分の必要な領域以外は使いませんので、最初に pull-in タイプの領域が確保され、その後 残った領域が push-out タイプのビューに使用されているようです。(実際の実装は、Apple にしかわかりません)

共存できないケース

pull-in タイプで、画面を占有してしまうとどうなるか 確認してみます。

Text に height: 900 を指定することで画面を占有してみます。

TextWith900Height


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

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing:0){
            Text("Hello world")
                .frame(height: 900)
                .background(Color.green)
            Color.yellow
        }
    }
}

Color.yellow は画面内に配置されていることが確認できません。

サイズ指定された要素の配置優先度が高いようです。つまり、最初にサイズ指定されている Text を配置した後に、Color を配置しようとしても、その時点で残り領域がなくなっているために、Color は、表示されないということな気がします。(繰り返しですが、実際の処理は、Apple にしかわかりません)

まとめ:SwiftUI の layout システムを理解する (push-out, pull-in)

SwiftUI の layout を理解するために、push-out / pull-in タイプのビューを説明しました。

SwiftUI の layout システムを理解する (push-out, pull-in)
  • push-out タイプのビューは、与えられた領域全体を使おうとする
  • pull-in タイプのビューは、自分が必要とする領域のみを使おうとする
  • pull-in タイプの要求する領域は、push-out タイプの要求する領域より 優先度高く処理される

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

SwiftUI 学習におすすめの本

SwiftUI 徹底入門

SwiftUI は、グラフィカルなライブラリということもあり、文字だけのテキストよりは、画像が多く入れられた書籍を読むと理解が進みやすいです。

自分で購入した中でおすすめできるものとしては、以下のものです。

2019 年発表の SwiftUI 1.0 相当を対象にしているので、2020/2021 に追加された一部の機能は、説明されていません。

ですが、SwiftUI 入門書としては、非常によくできていますし、わかりやすいです。 この本で学習した後に、追加分を学習しても良いと思います。

SwiftUIViewsMastery

英語での説明になってしまいますが、以下の本もおすすめです。

1ページに、コードと画面が並んでいるので、非常にわかりやすいです。

View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。

超便利です

SwiftUIViewsMastery

販売元のページは、こちらです。

コメントを残す

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