[Swift]SwiftUIチュートリアル Section5に入門してみた。[Handling User Input編]

Swift

はじめに

前回からの続きでSwiftUIのチュートリアル入門で、[Handling User Input編]になります。

もと記事サイト

Apple Developer Documentation

Step0

このセクションでは前回ModelをObservable Objectに変更したので、ModelとViewをバインドするためにViewに変更を加えていきます。

Step1

LandmarkList.swift@EnvironmentObject var modelData: ModelDataプロパティを追加します。
またLandmarkList_Previewsにも画面を表示する際にModelDataを参照するようにLandmarkList().environmentObject(ModelData)と変更します。

import SwiftUI

struct LandmarkList: View {
    @EnvironmentObject var modelData: ModelData
    @State private var showFavoritesOnly = false
    
    var filteredLandmarks: [Landmark] {
        landmarks.filter { landmark in
            (!showFavoritesOnly || landmark.isFavorite)
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                Toggle(isOn: $showFavoritesOnly) {
                    Text("Favorites only")
                }
                
                ForEach(filteredLandmarks) { landmark in
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
            .navigationTitle("Landmarks")
        }
    }
}

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .environmentObject(ModelData())
    }
}

Step2

Step1で変更を加えたことで、エラーが表示されているfilteredLandmarksプロパティのlandmarksmodelData.landmarksに変更します。

import SwiftUI

struct LandmarkList: View {
    @EnvironmentObject var modelData: ModelData
    @State private var showFavoritesOnly = false
    
    var filteredLandmarks: [Landmark] {
        modelData.landmarks.filter { landmark in
            (!showFavoritesOnly || landmark.isFavorite)
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                Toggle(isOn: $showFavoritesOnly) {
                    Text("Favorites only")
                }
                
                ForEach(filteredLandmarks) { landmark in
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
            .navigationTitle("Landmarks")
        }
    }
}

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkList()
            .environmentObject(ModelData())
    }
}

Step3

LandmarkDetail.swiftLandmarkDetail_PreviewsModelDataからデータを渡すように変更します。

import SwiftUI

struct LandmarkDetail: View {
    var landmark: Landmark
    
    var body: some View {
        ScrollView {
            MapView(coordinate: landmark.locationCoordinate)
                .ignoresSafeArea(edges: .top)
                .frame(height: 300)
            
            CircleImage(image: landmark.image)
                .offset(y: -60)
                .padding(.bottom, -60)
            
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack {
                    Text(landmark.park)
                        .font(.subheadline)
                    Spacer()
                    Text(landmark.state)
                        .font(.subheadline)
                }
                .font(.subheadline)
                .foregroundColor(.secondary)
                
                Divider()
                
                Text("About \(landmark.name)")
                    .font(.title2)
                Text(landmark.description)
            }
            .padding()
        }
        .navigationTitle(landmark.name)
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct LandmarkDetail_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: ModelData().landmarks[0])
    }
}

Step4

LandmarkRow.swiftLandmarkRow_Previewsもエラーが出ているので変更。

import SwiftUI

struct LandmarkRow: View {
    var landmark: Landmark
    
    var body: some View {
        HStack {
            landmark.image
                .resizable()
                .frame(width: 50, height: 50)
            Text(landmark.name)
            
            Spacer()
            
            if landmark.isFavorite {
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
            }
        }
    }
}

struct LandmarkRow_Previews: PreviewProvider {
    static var landmarks = ModelData().landmarks
    
    static var previews: some View {
        Group {
            LandmarkRow(landmark: landmarks[0])
            LandmarkRow(landmark: landmarks[1])
        }
        .previewLayout(.fixed(width: 300, height: 70))
    }
}

Step5

ContentView.swiftContentView_Previewsにも追加します。
追加することでプレビュー表示が可能になります。

import SwiftUI

struct ContentView: View {
    var body: some View {
        LandmarkList()
    }
}

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

Step6

SwiftUITestApp.swift@StateObjectModelDataを生成します。
ここで作られたModelDataが子ビューに渡されてViewで表示されます。(SwiftUITestAppはルートプロジェクトのファイルになります。プロジェクト+Appのファイルになります。)
ContentView().environmentObject(modelData)で生成したインスタンスをContentViewに渡しています。
ContentView自体にはModelDataのプロパティが無いためイニシャライズのパラメータとする必要がなく、そのまた下の子ViewにあたるLandmarkListで使用できるようにしています。

import SwiftUI

struct ContentView: View {
    var body: some View {
        LandmarkList()
    }
}

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

Step7

LandmarkList.swiftに戻ってライブプレビューモードでちゃんと動くか確認してみましょう!

問題なく動作しているようです!!

まとめ

  • @State
    単一のView内だけで使用可能。子Viewに受け渡したりはできないもの。
  • @ObservedObject
    自身のViewと子Viewまでに受け渡し可能。孫Viewまでは使用できない。
  • @EnvironmentObject
    子View以下まで受け渡し可能。Viewの階層が深い場合に使用。
  • @StateObject
    単一のインスタンスを生成する際に使用。シングルトン的なもの。

今日までに出てきたものをまとめるとこんか感じですかね。
なかなか混乱しますが、使用しながら慣れていけばいいかなと思います。

コメント

タイトルとURLをコピーしました