はじめに
前回からの続きでSwiftUIのチュートリアル入門で、[Handling User Input編]になります。
もと記事サイト
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
プロパティのlandmarks
をmodelData.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.swift
のLandmarkDetail_Previews
もModelData
からデータを渡すように変更します。
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.swift
のLandmarkRow_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.swift
のContentView_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
に@StateObject
でModelData
を生成します。
ここで作られた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
単一のインスタンスを生成する際に使用。シングルトン的なもの。
今日までに出てきたものをまとめるとこんか感じですかね。
なかなか混乱しますが、使用しながら慣れていけばいいかなと思います。
コメント