【SwiftUI】複数のViewを並べるにはどうすればいいか

はじめに

SwiftUIは、シンプルなコードでUIを構築できる強力なフレームワークです。

この記事では、これらのスタックの基本的な使い方と私が疑問に思った点を解説します。

その中でも、ビューを整理して配置するための基本的なツールとして「スタック(VStack、HStack、ZStack)」が提供されています。

各スタックの効果の違い

各スタックにはそれぞれ内部に保持するViewの配置方法が変わります。

  • VStack
    • 「縦」方向に内部のViewを配置していきます
  • HStack
    • 「横」方向に内部のViewを配置していきます
  • ZStack
    • 「手前」方向に内部のViewを配置していきます

サンプルコード

以下のコードは3種類のStackの内部で3つのTextを表示するコードになります。

それぞれのStackによる内部のViewの配置方法の違いがわかりやすいと思います。

Swift
struct ContentView: View {
  var body: some View {
    VStack{
      VStack{
        ForEach(1...3,id: \.self){ item in
          Text("VStack:\(item)")
        }
      }
      
      HStack{
        ForEach(1...3,id: \.self){ item in
          Text("HStack:\(item)")
        }
      }
      
      ZStack{
        ForEach(1...3,id: \.self){ item in
          Text("HStack:\(item)")
        }
      }
    }
  }
}

#Preview() {
  ContentView()
}

このコードのPreviewは以下の画像のようになります。
色のついている枠や文字は後からつけているものです。

気になったポイント

1. SwiftUIでfor文を使えない

私が最初にひかかったのはこの点です。Swiftの言語としての機能を使って以下のようなコードでTextを配置しようとしたところエラーが発生しました。
なぜできるかと思ったのかというと、もともと「Kotlin + Jetpack Compose」でAndroidアプリを作成していたからです。

Swift
VStack{
  for i in 1...3 {
    Text("VStack:\(i)")
  }
}

Zsh
Closure containing control flow statement cannot be used with result builder 'ViewBuilder'

このエラーは「ViewBuilder」内で「制御フロー文」を使おうとした場合に発生するエラーのようです。

SwiftUIでは、「ForEach」を使うことで「ループ」をすることができるようです。つまりは関数を使えということですね。

Swift
VStack {
  ForEach(1...3,id: \.self){ item in
    Text("VStack:\(item)")
  }
}

ただ指定した回数のループをさせるのにも、「id: \.self」という見慣れない引数をつけています。これの効果はまた別の記事で言及しようと思います。

とりあえずこの疑問の学びは以下の通りです。

「SwiftUIでは関数を使って制御をする」

2. bodyに渡すViewにVStack使わなくても大丈夫じゃない?

各Stackを知らないうちは以下のようなコードを記述しては、「なんで縦に並ぶのか」と疑問に思っていました。
(想像では、整列されずに上に重なるイメージ)

Swift
struct ContentView: View {
  var body: some View {
    Text("test1")
    Text("test2")
  }
}

よくよく調べてみると、以下のような事がわかりました。
公式には「some View」にStack系を使わないで複数のViewを返すことを想定しているような記述はないので、複数のViewを使う場合は、Stackで包むのがいいと思います。

SwiftUIではbodyプロパティで複数のViewを帰す場合「@ViewBuilder」が自動的に適応され、1つのViewにまとめられる

ここで「@ViewBuilder」と何かわからないと思います。
私もわからないのでまた別の記事でまとめる予定です。
記事を公開できたら、リンクを貼る予定です。(とりあえず公式のドキュメントを今は貼っておきます)

以下のForm(質問)も参考になるかも?

Stackを3つ組み合わせてみた

Swift
struct ContentView: View {
  var body: some View {
    VStack{
      //  深さを持ったスタック
      ZStack(alignment: .bottom){
        //  画像を表示(1番下)
        Image("UserIcon")
          .resizable() // 画像の横幅を画面に収まるように自動調整
          .aspectRatio(contentMode:  .fill)
          .frame(height: 300)
          .clipped()   // frameからはみ出した部分をクリップ
        HStack{
          VStack(alignment: .leading){
            Text("木村佳代子").font(.headline)
            Text("スーパー唇赤い女").font(.subheadline)
          }.padding()  // 何も指定しないと4方向に16ポイント
          //  スペーサーで横いっぱいにHStackを広げる
          Spacer()
        }
        //  前面で使う色を指定
        .foregroundColor(.primary)
        //  背景で使う色を前面で使う色の反転色に設定
        .background(
          Color.primary.colorInvert()
            .opacity(0.75) // 少し透明にする
        )
      }
    }
  }
}

#Preview() {
  ContentView()
}
Screenshot

おわりに

SwiftUIはAndroidのJetpack Composeと比べて言語仕様をそのまま使うことができない部分がある反面、記述が少なくても作り込めるようにできているのが特徴だと感じました。

認識間違いや、不明な点がある場合はXかこの記事のコメント欄でご連絡ください。

参考

コメントを残す

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

CAPTCHA