Chapter 04無料公開

【Part3】SwiftUIの基本である「VStack・HStack・ZStack」を覚えよう

Rikuto Sato
Rikuto Sato
2022.12.19に更新

YouTubeで見る

このPartでは、VStackHStackZStackについて解説していきたいと思います。

コードの構成について

VStackHStackZStackについて解説する前に、コードの構成について解説していきたいと思います。
まず、プロジェクトを作成すると、二つのプログラムファイルが生成されます。

プロジェクト名App.swiftと、ContentView.swiftの二つです。

プロジェクト名App.swift

中身は以下のようになっています。

import SwiftUI

@main
struct HelloSwiftUIApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}


ブロック単位で見ていくとこのようになっています。structの中に、var bodyがあって、さらにその中にWindowGroupがあるという構成です。
このファイルでは、何をしているのかというと、最初に表示する画面を決めているだけです。

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


ブロック単位で見ていくと、2つに分かれています。
上のブロックは、struct ContentViewの中にvar bodyがあります。
下のブロックは、struct ContentView_Previewsの中に、static var previewsがあります。

このファイルは、最初に表示される画面のレイアウトや処理を書くためのファイルです。
上のブロックのvar bodyの中に色々とオブジェクトを書いてレイアウトを作っていきます。

では、下のブロックは何かというと、下のブロックは、プレビューを表示させるコードになります。試しに、削除してみるとプレビュー部分が消えると思います。なので、プレビュー機能を使わないならこのコードは必要ありません。

VStack、HStack、ZStackとは?

まずは、SwiftUIの基本中の基本である3つのStackを覚えるところから始めましょう。

Textを縦に三つ並べるときに、var bodyの中にTextを3つ書くと、以下のようにあいうえおしか表示されません。
では、かきくけこさしすせそはどこに行ったのかというと、上に表示されているContentViewを押すとそれぞれ表示されます。

では、3つのTextVStackの中に入れてみましょう。

VStack {
    Text("あいうえお")
    Text("かきくけこ")
    Text("さしすせそ")
}
追記する場所

上記のように記載すると、縦並びになると思います。
SwiftUIはこのように、オブジェクトをStackにどんどん入れてレイアウトを作成していきます。

Stackというのは、並べていれる箱だと思ってください。
じゃあ、VStackというのは、VはVerticalの略なので、縦に並べる箱です。
じゃあ、HStackというのは、HはHorizontalの略なので、横に並べる箱です。
じゃあ、ZStackというのは、ZはZ軸とかのZの意味なので、重ねて並べる箱です。

まずはこの3つのStackをしっかりと覚えましょう。

VStack、HStack、ZStackを使ってみる

では、以下のようなサイズが異なる3つの四角をVStackHStackZStackに入れてみます。

四角のViewについて

Rectangle()で四角のViewが作れます。
.foregroundColor(.blue)でViewの色を決められます。
.frame(width: 200, height: 200)で幅と高さを決めることができます。

Rectangle()
    .foregroundColor(.blue)
    .frame(width: 200, height: 200)
Rectangle()
    .foregroundColor(.red)
    .frame(width: 150, height: 150)
Rectangle()
    .foregroundColor(.yellow)
    .frame(width: 100, height: 100)

Stackに入れてみる

これらをそれぞれのStackに入れていくと以下のようになります。

VStackのコード
struct ContentView: View {
    var body: some View {
        VStack {
            Rectangle()
                .foregroundColor(.blue)
                .frame(width: 200, height: 200)
            Rectangle()
                .foregroundColor(.red)
                .frame(width: 150, height: 150)
            Rectangle()
                .foregroundColor(.yellow)
                .frame(width: 100, height: 100)
        }
    }
}
HStackのコード
struct ContentView: View {
    var body: some View {
        HStack {
            Rectangle()
                .foregroundColor(.blue)
                .frame(width: 150, height: 150)
            Rectangle()
                .foregroundColor(.red)
                .frame(width: 100, height: 100)
            Rectangle()
                .foregroundColor(.yellow)
                .frame(width: 50, height: 50)
        }
    }
}
ZStackのコード
struct ContentView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .foregroundColor(.blue)
                .frame(width: 200, height: 200)
            Rectangle()
                .foregroundColor(.red)
                .frame(width: 150, height: 150)
            Rectangle()
                .foregroundColor(.yellow)
                .frame(width: 100, height: 100)
        }
    }
}

少し複雑なレイアウトを作ってみる

では、このようなレイアウトを作成してみましょう。

では、このような画面を作るとなると、どのように考えていくかというと、以下の画像のような感じで作っていきます。

  1. VStackの中に、グレーと紫とHStackZStackが入っています。
  2. HStackには、青と赤と黄色のViewが入っています。
  3. ZStackには、緑とHStackが重なって入っています。
  4. Hstackには、VStackが二つ入っています。
  5. 左のVStackにはピンクと黒のViewが入っていて、右のVStackには白と黄色のViewが入っています。

という構成です。ちょっとややこしいですが、こんな感じにレイアウトを組んでいくのがSwiftUIです。

struct ContentView: View {
    var body: some View {
        VStack {
            Rectangle()
                .foregroundColor(.gray)
                .frame(width: 350, height: 100)
            Rectangle()
                .foregroundColor(.purple)
                .frame(width: 350, height: 100)
            HStack {
                Rectangle()
                    .foregroundColor(.blue)
                    .frame(width: 150, height: 150)
                Rectangle()
                    .foregroundColor(.red)
                    .frame(width: 100, height: 100)
                Rectangle()
                    .foregroundColor(.yellow)
                    .frame(width: 50, height: 50)
            }
            ZStack {
                Rectangle()
                    .foregroundColor(.green)
                    .frame(width: 350, height: 200)
                HStack {
                    VStack {
                        Rectangle()
                            .foregroundColor(.pink)
                            .frame(width: 150, height: 70)
                        Rectangle()
                            .foregroundColor(.black)
                            .frame(width: 150, height: 70)
                    }
                    VStack {
                        Rectangle()
                            .foregroundColor(.white)
                            .frame(width: 150, height: 70)
                        Rectangle()
                            .foregroundColor(.yellow)
                            .frame(width: 150, height: 70)
                    }
                }
            }
        }
    }
}

こちらは、どのViewがどこに書かれているかというのがわかる図解です。

演習問題

次は、問題形式で、VStackHStackZStackの使い方について解説してきたいと思います。

問題①

以下のようなレイアウトを組んでみましょう。(ハンバーガーのつもり)

答え
struct ContentView: View {
    var body: some View {
        VStack {
            Rectangle()
                .foregroundColor(.orange)
                .frame(width: 200, height: 50)
            Rectangle()
                .foregroundColor(.red)
                .frame(width: 180, height: 20)
            Rectangle()
                .foregroundColor(.yellow)
                .frame(width: 180, height: 20)
            Rectangle()
                .foregroundColor(.brown)
                .frame(width: 180, height: 20)
            Rectangle()
                .foregroundColor(.green)
                .frame(width: 180, height: 20)
            Rectangle()
                .foregroundColor(.orange)
                .frame(width: 200, height: 50)
        }
    }
}

問題②

以下のようなレイアウトを組んでみましょう。(クリーパーのつもり)

答え
struct ContentView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .foregroundColor(.green)
                .frame(width: 300, height: 300)
            VStack {
                HStack {
                    Rectangle()
                        .foregroundColor(.black)
                        .frame(width: 70, height: 70)
                    Rectangle()
                        .foregroundColor(.black)
                        .frame(width: 70, height: 70)
                }
                Rectangle()
                    .foregroundColor(.black)
                    .frame(width: 50, height: 20)
                Rectangle()
                    .foregroundColor(.black)
                    .frame(width: 100, height: 80)
            }
        }
    }
}

問題③

以下のようなレイアウトを組んでみましょう。(ミニオンのつもり)

答え
struct ContentView: View {
    var body: some View {
        VStack {
            ZStack {
                Rectangle()
                    .foregroundColor(.yellow)
                    .frame(width: 300, height: 300)
                VStack {
                    HStack {
                        Rectangle()
                            .foregroundColor(.black)
                            .frame(width: 60, height: 20)
                        ZStack {
                            Rectangle()
                                .foregroundColor(.gray)
                                .frame(width: 90, height: 90)
                            Rectangle()
                                .foregroundColor(.white)
                                .frame(width: 70, height: 70)
                            Rectangle()
                                .foregroundColor(.black)
                                .frame(width: 20, height: 20)
                        }
                        ZStack {
                            Rectangle()
                                .foregroundColor(.gray)
                                .frame(width: 90, height: 90)
                            Rectangle()
                                .foregroundColor(.white)
                                .frame(width: 70, height: 70)
                            Rectangle()
                                .foregroundColor(.black)
                                .frame(width: 20, height: 20)
                        }
                        Rectangle()
                            .foregroundColor(.black)
                            .frame(width: 60, height: 20)
                    }
                    ZStack {
                        Rectangle()
                            .foregroundColor(.black)
                            .frame(width: 70, height: 40)
                        VStack {
                            Rectangle()
                                .foregroundColor(.white)
                                .frame(width: 60, height: 10)
                            Rectangle()
                                .foregroundColor(.red)
                                .frame(width: 60, height: 10)
                        }
                    }
                }
            }
            Rectangle()
                .foregroundColor(.blue)
                .frame(width: 300, height: 100)
            HStack {
                Rectangle()
                    .foregroundColor(.black)
                    .frame(width: 70, height: 30)
                Rectangle()
                    .foregroundColor(.black)
                    .frame(width: 70, height: 30)
            }
        }
    }
}

これで演習問題は終わりです。
最後のミニオンは激ムズだと思います。
コードの詳しい解説については動画の方でしているので時間がある方は動画の方もご確認ください。