Sponsor Link
環境&対象
- macOS Monterey 12.1 RC
- Xcode 13.2 RC
- iOS 15.2
SwiftUI でのレイアウト
これまでに、領域が十分あるケースで、push-out/pull タイプのビューがレイアウトされる時の挙動をみてきました。
[SwiftUI] SwiftUI の layout システムを理解する (コンテナビューに含まれる push-out / pull-in タイプビューの調停) [SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビューの調停) [SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビュー, pull-in タイプビュー とレイアウトの調整)
今回は、レイアウトする領域が、十分に広くないケースでの振る舞いを確認します。
領域が不足しているケースでのレイアウト
領域が不足している とは?
以前の記事でも書きましたが、親ビューが与える領域は、子ビューが実際に必要とする領域の大きさを考慮しません。
[SwiftUI] SwiftUI の layout システムを理解する (基礎編: レイアウトシーケンス)
子ビューは、必要ならば親ビューから与えられた領域を考慮して自分の領域を決定し、そこに表示を行うだけです。
親ビューと領域について ”もっと高くしたい” や ”もう少し狭くても良い” 等のやりとりはしません。
ですので、”領域が不足するケース” とは、親ビューから与えられた領域が ”親ビューから提案されたサイズをベースに自分で決定した使用領域が 自分自身を描画するのに十分でないケース” です。
例えば、Text は、領域の幅が不足していると、複数行に変更したり、文字列の一部を … という表示に変更したりします。
ですが、これは 個別のビューの振る舞いですので、今回の確認範囲とはしません。
今回は、以下のようなケースでの振る舞いを確認していきます。
- 可変高さを持つ複数の子要素を持つ VStack のレイアウト
- 固定高さを持つ複数の子要素を持つ VStack のレイアウト
- 固定高さと可変高さを持つ子要素を持つ VStack のレイアウト
可変高さを持つ要素とは、push-out タイプのビューを指していて、固定高さを持つ要素とは pull-in タイプのビューを指しています。
pull-in タイプのビューとして、Text を .frame で高さを固定したビューを使って確認していきます。
push-out タイプのビューは、Color を使います。
可変高さを持つ複数の子要素を持つ VStack のレイアウト
一番わかりやすいケースから考えます。
VStack に高さが与えられているならば、等分した高さを与えてレイアウトします。
与えられる高さが存在する(つまり 0 より大きい時)は、(表示が収まるかは別として)可変高さを持つ要素としてはレイアウトできます。
これは、以前に確認したケースです。
ですので、領域が不足するということは、layout システム上は、存在しないはずです。
固定高さを持つ複数の子要素を持つ VStack のレイアウト
2個の固定高さ(300)を持つ Text を含む VStack(高さ500)のレイアウトで確認してみます。
簡略化すると以下のようなビューです。
VStack {
Text("Hello").frame(height:300)
Text("world").frame(height:300)
}.frame(height: 500)
下のスクリーンショットを見るとわかりますが、VStack に収まり切らないようにレイアウトされています。
(オレンジ色の枠が、VStack の枠です)
# .clipped を付与すると、上下が切られてしまうことも確認できます。
実際のコードは以下のものを使っています。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2021/12/10
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing:0) {
Text("Hello")
.frame(height: 300).frame(maxWidth: .infinity)
.border(.red)
.overlay {
GeometryReader { geom in
Text("\(geom.size.width) - \(geom.size.height)")
}
}
Text("world")
.frame(height: 300).frame(maxWidth: .infinity)
.border(.blue)
.overlay{
GeometryReader { geom in
Text("\(geom.size.width) - \(geom.size.height)")
}
}
}
.frame(height: 500)
.border(.orange, width: 4)
// .clipped()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
何が起きているのか?
# 厳密な意味での振る舞いは、開示されていないので、Apple しかわかりません。以下、推測です。
2つの Text はそれぞれ 高さ 300 と指定されています。ですので、VStack としては、それを縦に積み重ねて 600 の高さを持つようなレイアウトにして、上位のビューに回答しているように見えます。
以下は、上記に至るまでの考えです。
Text は、上位ビューからのおすすめ高さを無視して、自分に必要な領域を返します。今回のケースでは、300 を使うと親ビューにも返し、実際 300 の高さを使用します。
このことは、以前に説明しました。
[SwiftUI] SwiftUI の layout システムを理解する (基礎編: レイアウトシーケンス)
以下の記事で確認したように、子ビューとして pull-in タイプのビューのみを持つ VStack は、pull-in 的なレイアウトになっていました。
[SwiftUI] SwiftUI の layout システムを理解する (コンテナビューに含まれる push-out / pull-in タイプビューの調停)
これらの点を合わせて考えると、VStack も Text と同様の振る舞いをして、自分が必要と考える領域を使用していると推測できます。
具体的な振る舞いとしては VStack は、上位ビューからのおすすめ高さは無視して、下位ビューが必要とする 300 + 300 = 600 の高さを使用するということです。
# .clipped 指定されるとはみ出た部分は削除されますが、それは、親ビュー側の操作です。
固定高さと可変高さを持つ子要素を持つ VStack のレイアウト
1つの固定高さ(600)を持つビューと、push-out タイプのビューで試してみます。
VStack {
Text("Hello").frame(height:600)
Color.red
}.frame(height:500)
以下のような結果となり、VStack からはみ出す Text が配置され、Color.red は配置されていないことがわかります。
実際に動かしたコードは以下です。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2021/12/10
// © 2021 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing:0) {
Text("Hello")
.frame(height: 600).frame(maxWidth: .infinity)
.border(.red)
.overlay {
GeometryReader { geom in
Text("\(geom.size.width) - \(geom.size.height)")
}
}
Color.red
}
.frame(height: 500)
.border(.orange, width: 4)
}
}
何が起きているのか?
結果から推測するに以下のようなことになっている気がします。
- VStack の子ビューを確認する
- 最初の子ビュー Text の高さを確認すると 600、もう一つのビューは、Color で push-out タイプ
- VStack は高さ 500 と指定されている
- Text を配置するとすでに 500 を超えて 600 になるので、Color は、レイアウトしない
若干の考察
Apple のドキュメントにはざっくりしか記述されていないので、推測ばかりです。
ですが、レイアウトシステムをできるだけシンプルにしようとすると、これまでの推測が 大きく外れてはいない気がします。(気がするだけですが)
まとめ:layout システムを理解する (領域が不足するケースの調停)
- 領域が不足すると、push-out タイプのビューがレイアウトされなくなる
- 領域が不足しても、pull-in タイプのビューは、不足に構わずレイアウトされる
- 領域が不足しているケースで push-out/pull-in 両方のタイプのビューがあると、まずは、push-out タイプがレイアウトされなくなる
[SwiftUI] SwiftUI の layout システムを理解する (準備編: border と GeometryReader ) [SwiftUI] SwiftUI の layout システムを理解する (基礎編: レイアウトシーケンス) [SwiftUI] SwiftUI の layout システムを理解する ( .frame の働き) [SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビュー, pull-in タイプビュー とレイアウトの調整) [SwiftUI] SwiftUI の layout システムを理解する (push-out タイプビューの調停) [SwiftUI] SwiftUI の layout システムを理解する (コンテナビューに含まれる push-out / pull-in タイプビューの調停) [SwiftUI] SwiftUI の layout システムを理解する (Text)
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI 学習におすすめの本
SwiftUI 徹底入門
SwiftUI は、グラフィカルなライブラリということもあり、文字だけのテキストよりは、画像が多く入れられた書籍を読むと理解が進みやすいです。
自分で購入した中でおすすめできるものとしては、以下のものです。
2019 年発表の SwiftUI 1.0 相当を対象にしているので、2020/2021 に追加された一部の機能は、説明されていません。
ですが、SwiftUI 入門書としては、非常によくできていますし、わかりやすいです。 この本で学習した後に、追加分を学習しても良いと思います。
SwiftUIViewsMastery
英語での説明になってしまいますが、以下の本もおすすめです。
1ページに、コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
Sponsor Link