LoginSignup
0
2

More than 3 years have passed since last update.

SwiftUIで複数のViewに被るViewを実装する

Last updated at Posted at 2020-08-17

これはなに

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のサイズと連動させるのは、
別階層同士が密結合になっていてあまりよくなさそうです。

もっと綺麗に連動できる方法があれば知りたいです

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2