📝

SwiftUI チュートリアルをやってみる - Creating and Combining Views

2022/10/23に公開

SwiftUI チュートリアル

SwiftUIの学習の為、チュートリアルを丁寧にやってみようと思います。
今回は Chapter 1 - SwiftUI EssentialsCreating and Combining Views をやってみます。

試した環境

- Xcode: 14.0.1
- MacBook Pro (13-inch, M1, 2020)
  - macOS Monterey 12.6
  - メモリ 16 GB

Creating and Combining Views

Viewをどの様に構築していくかを Landmark アプリのランドマーク詳細画面を作成しながら進めていきます。

Section1 - Create a New Project and Explore the Canvas

XcodeでSwiftUIを使用するプロジェクトを作成します。

  • Step1

    • Xcodeを開き Create a new Xcode project を選択します
  • Step2

    • テンプレートから iOS App を選択します
  • Step3

    • ※ チュートリアルでは InterfaceLanguage を選択できるようなUIを説明していたが、最新のXcodeだとデフォルトでSwiftUI+Swiftが選択されておりました
  • Step4

    • SwiftUIのアプリではAppプロトコルに準拠した構造体を実装します

      • bodyプロパティでは1つ以上のSceneを返します
      • @main はエントリポイントを示します
      import SwiftUI
      
      @main
      struct LandmarksApp: App {
          var body: some Scene {
              WindowGroup {
                  ContentView()
              }
          }
      }
      
  • Step5

    • SwiftUIのViewファイルではデフォルト2つの構造体が定義されています
      • 1つ目は View プロトコルに準拠した構造体
      • 2つ目は Preview用の構造体
    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()
        }
    }
    
  • Step6

    • 選択しているデバイスでPreviewが変わります :sparkles:
      • DeviceがMacの場合

      • DeviceがiPhoneの場合

  • Step7

    • Textの文字列を修正するとPreviewも変わります

Section2 - Customize the Text View

コードやインスペクタを使用して Text View をカスタムします。

  • Step1 - 3

    • inspectorを使って文字列やfontを変更します (その他色々機能があります)

    • ソースコードは以下に修正されていました

                  Text("Hello")
                      .font(.title)
      
  • Step4

    • テキストのカラーを green に変更します
                  Text("Hello")
                      .font(.title)
                      .foregroundColor(.green)
      
  • Step5

    • 先ほどのinspectorがコード上からでも表示できます

Section3 - Combine Views Using Stacks

Viewをグループ化するスタックを使用してレイアウトを調整します。

  • Step1-2

    • Embed in VStack で VStackの中にTextを格納し、新たなTextを追加します
  • Step4-5

    • VStack(alignment: .leading) とすることで左寄せにできます
  • Step6 - 7

    • HStack追加し、新たなTextを追加します
  • Step8

    • Spacer を追加して横方向の間にスペースを置来ます
                  HStack {
                      Text("Joshua Tree National Park")
                          .font(.subheadline)
                      Spacer()
                      Text("California")
                          .font(.subheadline)
                  }
      

  • Step9
    • 最後に paddingVStack に追加します
              VStack(alignment: .leading) {
                  Text("Turtle Rock")
                      .font(.title)
                  HStack {
                      Text("Joshua Tree National Park")
                          .font(.subheadline)
                      Spacer()
                      Text("California")
                          .font(.subheadline)
                  }
              }.padding()
      

Section 4 - Create a Custom Image View

Image Viewをカスタムします。

  • Step1

    • 新規画像リソースを作成、リソース名を turtlerock に変更します
  • Step2

    • 新しいSwiftUI Viewファイルを作成します
  • Step3

    • Text("Hello, World!")Image に変更します
      struct CircleImage: View {
          var body: some View {
              Image("turtlerock")
          }
      }
      
  • Step4

    • clipShape(Circle()) を追加して円形にします
      struct CircleImage: View {
          var body: some View {
              Image("turtlerock").clipShape(Circle())
          }
      }
      

  • Step5 - 7
    • overlay modifier を利用して円に枠をつけます
      • 色を白色に変更
      • shadowをつける (radius: 7)
        struct CircleImage: View {
            var body: some View {
                Image("turtlerock")
                    .clipShape(Circle())
                    .overlay {
                        Circle().stroke(.white, lineWidth: 4)
                    }.shadow(radius: 7)
            }
        }
        

Section 5 - Use SwiftUI Views From Other Frameworks

MapKitMapView を使用し地図を表示します。

  • Step1

    • 新規 SwiftUI ViewMapView.swift というファイル名で作成します
  • Step2

    • import MapKit を追加します
  • Step3

    • @State アトリビュートを使用し、マップのRegionを保持するプライベートステート変数を作成します

      import SwiftUI
      import MapKit
      
      struct MapView: View {
          @State private var region = MKCoordinateRegion(
              center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
              span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
          )
      
          var body: some View {
              Text("Hello, World!")
          }
      }
      
      struct MapView_Previews: PreviewProvider {
          static var previews: some View {
              MapView()
          }
      }
      
  • Step4

    • Text("Hello, World!") の部分を Map(coordinateRegion: $region) に置き換える
    • プライベートステート変数に $ を指定して設定すると binding され状態に応じて更新される様になります
  • Step5

    • Live Preview モードを選択するとプレビューで実際にMapを触ることができます。

Section 6 - Compose the Detail View

これまでのViewを組み合わせLandmark詳細ビューの最終デザインを作成します

  • Step1

    • Project navigatorから ContentView.swift を開きます
  • Step2

    • 一番外側に VStack を追加します
  • Step3

    • 以前作成した MapView を先ほど追加した VStack の先頭に追加します

      • frame(width:height:). を用いて MapView のサイズを固定します
      • ※ heightのみを指定した場合、Viewはそのコンテンツの幅に合わせて自動的にサイズ調整されます。
        • この場合、MapViewは利用可能なスペースを埋めるように拡張されます。
      struct ContentView: View {
          var body: some View {
              VStack {
                  MapView().frame(height: 300) // 追加
                  VStack(alignment: .leading) {
                      Text("Turtle Rock")
                          .font(.title)
                      HStack {
                          Text("Joshua Tree National Park")
                              .font(.subheadline)
                          Spacer()
                          Text("California")
                              .font(.subheadline)
                      }
                  }.padding()
              }
          }
      }
      

  • Step4

    • ここでも Live Preview モードで確認できます
  • Step5

    • 以前作成した CircleImageMapView の下に追加します

      struct ContentView: View {
          var body: some View {
              VStack {
                  MapView().frame(height: 300)
                  CircleImage() // 追加
                  VStack(alignment: .leading) {
                      Text("Turtle Rock")
                          .font(.title)
                      HStack {
                          Text("Joshua Tree National Park")
                              .font(.subheadline)
                          Spacer()
                          Text("California")
                              .font(.subheadline)
                      }
                  }.padding()
              }
          }
      }
      

  • Step6
    • offsetpadding を使用して画像の位置を調整します

                  CircleImage()
                      .offset(y: -130)
                      .padding(.bottom, -130)
      

  • Step7
    • 外側の VStack の一番下に Spacer を追加し、コンテンツを上部に押し上げます

      struct ContentView: View {
          var body: some View {
              VStack {
                  MapView().frame(height: 300)
                  CircleImage()
                      .offset(y: -130)
                      .padding(.bottom, -130)
                  VStack(alignment: .leading) {
                      Text("Turtle Rock")
                          .font(.title)
                      HStack {
                          Text("Joshua Tree National Park")
                              .font(.subheadline)
                          Spacer()
                          Text("California")
                              .font(.subheadline)
                      }
                  }.padding()
                  Spacer() // 追加
              }
          }
      }
      

  • Step8
    • MapView を画面の上端まで広げる為 ignoresSafeArea(edges: .top) を追加します

                  MapView()
                      .ignoresSafeArea(edges: .top) // 追加
                      .frame(height: 300)
      

  • Step9
    • Divider を設置し詳細を追加します

      struct ContentView: View {
          var body: some View {
              VStack {
                  MapView()
                      .ignoresSafeArea(edges: .top)
                      .frame(height: 300)
                  CircleImage()
                      .offset(y: -130)
                      .padding(.bottom, -130)
                  VStack(alignment: .leading) {
                      Text("Turtle Rock")
                          .font(.title)
                      HStack {
                          Text("Joshua Tree National Park")
                              .font(.subheadline)
                          Spacer()
                          Text("California")
                              .font(.subheadline)
                      }
                      Divider() // 追加
                      Text("About Turtle Rock")
                          .font(.title2)  // 追加
                      Text("Descriptive text goes here.")  // 追加
                  }.padding()
                  Spacer()
              }
          }
      }
      

  • Step10
    • グループに対して指定する

                      HStack {
                          Text("Joshua Tree National Park")
                              .font(.subheadline)
                          Spacer()
                          Text("California")
                              .font(.subheadline)
                      }
      
    • 上記部分を以下に変更する

                      HStack {
                          Text("Joshua Tree National Park")
                          Spacer()
                          Text("California")
                      }
                      .font(.subheadline) // 共通する部分を外だし
                      .foregroundColor(.secondary) // カラーの設定
      

Discussion