これはなに
SwiftUIで複数のViewの上に、
複雑な表示を実装しようとしたときに調べた内容をメモします。
環境: Xcode11.5、iOS Simulator 13.xx
Viewの上にViewを配置する
例えば以下のようなViewが二つ横並びの表示があったとします。
コードは以下の感じです。
struct Cell: View {
var body: some View {
VStack(alignment: .center) {
Spacer()
HStack(alignment: .center) {
Spacer()
Text("Cell")
Spacer()
}
Spacer()
}.border(Color.black)
}
}
struct ZStackPlaygroundView: View {
var body: some View {
HStack {
ForEach (0..<2) { _ in
Cell()
}
}
}
}
このView二つに被せて何かViewを上に載せたいとします。
例えば以下のような赤のラベルを重ねるなどです。
Cellに直接赤ラベルを配置しようとしてもうまくいきません。
struct HasRedLabelCell: View {
var body: some View {
VStack(alignment: .center) {
Spacer()
HStack(alignment: .center) {
Spacer()
// 赤ラベルをサイズを指定して追加
Text("Cell").frame(width: 200).background(Color.red)
Spacer()
}
Spacer()
}.border(Color.black)
}
}
struct ZStackPlaygroundView: View {
var body: some View {
ZStack(alignment: .top) {
HStack {
// ❗️赤ラベルをサイズの分だけHasRedLabelCellが大きくなるので二つのViewに重なる形で表示できない。
HasRedLabelCell()
Cell()
}
}
}
}
なぜなら、子Viewの大きさに合わせて親Viewの大きさが変わるからです。
(親Viewからはみ出す方法はあるかもしれませんが、調べてないです)
なので親子関係にせずに、ZStackを使って同一階層にします。
ZStackを使うことによって、上にViewを配置することができます。
struct ZStackPlaygroundView: View {
var body: some View {
// ZStackを追加
ZStack(alignment: .top) {
HStack {
ForEach (0..<2) { _ in
Cell()
}
}
// ここのViewを二つのView(struct Cell)の上に被せる
HStack {
Spacer()
Text("Label").colorInvert()
Spacer()
}
.background(Color.red)
.padding(.top, 250)
}
}
}
横幅の割合からViewの大きさを決める
親Viewの大きさに合わせてViewのサイズを変更したいとします。
このとき親Viewの大きさを動的に取得する方法にGeometryReaderを使う方法があります。
GeometryReaderを使えば、親Viewのサイズなどのレイアウト情報を取得できます。
先程のZStackとGeometryReaderを組み合わせれば、親Viewに対して3/4の横幅にしてViewを被せるなど動的にサイズを変更できます。
実際のコードは以下の感じです。
struct ZStackPlaygroundView: View {
var body: some View {
ZStack(alignment: .top) {
// GeometryReaderを使って、親Viewのサイズを取得
GeometryReader { geometry in
HStack {
ForEach (0..<2) { _ in
Cell()
}
}
HStack {
Spacer()
Text("Label").colorInvert()
Spacer()
}
// 親Viewのサイズの3/4を横幅として設定
.frame(width: geometry.size.width * 0.75)
.background(Color.red)
.padding(.top, 250)
}
}
}
}
さらに頑張れば段違いのちょっと複雑なViewも組むことができます。
実際のサンプルはGitHubにおいてあるので、気になる方は見てみてください。
https://github.com/rnishimu22001/SwiftUIPlayground/blob/master/SwiftUIPlayground/GeometoryPlaygroundView.swift
まとめ
- Viewの上に何かViewをおきたければZStackを使う
- 親Viewのサイズを使って何かしたければGeomeotryReaderを使う
ZStackやGeometryReaderで別階層のViewのサイズと連動させるのは、
別階層同士が密結合になっていてあまりよくなさそうです。
もっと綺麗に連動できる方法があれば知りたいです