#author("2022-07-26T05:51:43+00:00","default:src128","src128")
#author("2022-07-26T05:52:31+00:00","default:src128","src128")
&tag(SwiftUI/日本語チュートリアル3);
*目次 [#uef95f88]
#contents
*関連ページ [#of5c6dfe]
*参考情報 [#j9044e8e]
-[[【第3回】日本語版SwiftUIチュートリアル【ユーザ入力を扱う】 | HIRO LAB BLOG:https://hirlab.net/nblog/category/programming/art_1445/]]

*第3回:ユーザ入力を扱う [#p1070df4]
**Step 1. お気に入り機能を実装する [#a6473434]
-LandmarkにisFavoriteを追加。
  var isFavorite: Bool
-LandmarkRow.swiftにUIを追加。
#pre{{
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")
                            .imageScale(.medium)
                            .foregroundColor(.yellow)
            }
        }
    }
}}

**Step 2. リストのフィルタリング [#a92e9d64]
-お気に入りだけを静的に表示する。
#pre{{
struct LandmarkList: View {
    var showFavoritesOnly: Bool = true
    
    var body: some View {
        NavigationView{
            List(landmarkData) { landmark in
                if !self.showFavoritesOnly || landmark.isFavorite{
                    NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                        LandmarkRow(landmark: landmark)
                    }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
}}
-ForEachを入れるときれいになるらしい。
#pre{{
struct LandmarkList: View {
    var showFavoritesOnly: Bool = true
    
    var body: some View {
        NavigationView{
            List {
                ForEach(landmarkData) { landmark in
                    if !self.showFavoritesOnly || landmark.isFavorite {
                        NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                            LandmarkRow(landmark: landmark)
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
}}

** Step 3. フィルタリングの切替 [#l9712c97]
-showFavoritesOnlyに@Stateを付与。さらにトグルを追加。$showFavoritesOnlyに注意。
#pre{{

struct LandmarkList: View {
    @State var showFavoritesOnly: Bool = true
    
    var body: some View {
        NavigationView{
            List {
                Toggle(isOn: $showFavoritesOnly) {
                    Text("Favorites only")
                }
                
                ForEach(landmarkData) { landmark in
                    if !self.showFavoritesOnly || landmark.isFavorite {
                        NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                            LandmarkRow(landmark: landmark)
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
}}

** Step 4. 監視可能オブジェクトの定義 [#g8ef3303]
-監視可能オブジェクトを導入する。
-ModelsディレクトリにUserData.swiftを作成する。
#pre{{
import SwiftUI
import Combine
 
final class UserData: ObservableObject  {
    @Published var showFavoritesOnly = false
    @Published var landmarks = landmarkData
}
}}

** Step 5. 監視可能オブジェクトをビューに適応させる [#n96fa7ad]
-環境オブジェクトを追加していく。
-LandmarkList.swift
#pre{{

struct LandmarkList: View {
//    @State var showFavoritesOnly: Bool = true
    @EnvironmentObject var userData: UserData
    
    var body: some View {
        NavigationView{
            List {
                Toggle(isOn: $userData.showFavoritesOnly) {
                    Text("Favorites only")
                }
                
                ForEach(userData.landmarks) { landmark in
                    if !self.userData.showFavoritesOnly || landmark.isFavorite {
                        NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                            LandmarkRow(landmark: landmark)
                        }
                    }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}


struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone X"], id: \.self) { deviceName in
            LandmarkList()
                .previewDevice(PreviewDevice(rawValue: deviceName))
                .previewDisplayName(deviceName)
                .environmentObject(UserData())
        }
    }
}
}}
-LandmarksApp.swiftの修正。
#pre{{
@main
struct LandmarksApp: App {
    var body: some Scene {
        WindowGroup {
            LandmarkList().environmentObject(UserData())
        }
    }
}
}}
-LandmarkDetail.swiftの修正。
#pre{{
struct LandmarkDetail: View {
    @EnvironmentObject var userData: UserData
    var landmark: Landmark
    
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .edgesIgnoringSafeArea(.top)
                .frame(height: 300)
            
            CircleImage(image: landmark.image)
                .offset(y: -130)
                .padding(.bottom, -130)
            
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .font(.title)
                HStack {
                    Text(landmark.park)
                        .font(.subheadline)
                    
                    Spacer()
                    
                    Text(landmark.state)
                        .font(.subheadline)
                }
            }
            .padding()
            
            Spacer()
        }
    }
}
 
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        LandmarkDetail(landmark: landmarkData[0]).environmentObject(UserData())
    }
}

}}

** Step 6. お気に入りボタンの追加・削除 [#l01df9d6]
-詳細ビューからもお気に入りの追加と削除ができるようにする。
-LandMarkDetailにインデックスを計算できるメソッドを追加。
#pre{{
    var landmarkIndex: Int {
        userData.landmarks.firstIndex(where: { $0.id == landmark.id })!
    }
}}

#pre{{
    var body: some View {
        VStack {
            MapView(coordinate: landmark.locationCoordinate)
                .edgesIgnoringSafeArea(.top)
                .frame(height: 300)
            
            CircleImage(image: landmark.image)
                .offset(y: -130)
                .padding(.bottom, -130)
            
            VStack(alignment: .leading) {
                HStack {
                    Text(landmark.name)
                        .font(.title)
                    
                    Button(action: {
                        // isFavoriteを切り替える
                        self.userData.landmarks[self.landmarkIndex].isFavorite.toggle()
                    }) {
                        // もしお気に入りならば
                        if self.userData.landmarks[self.landmarkIndex].isFavorite {
                            // 塗り潰しされたスター
                            Image(systemName: "star.fill")
                                .foregroundColor(Color.yellow)
                        } else {
                            // そうでなければ白抜きスター
                            Image(systemName: "star")
                                .foregroundColor(Color.gray)
                        }
                    }
                }
                HStack {
                    Text(landmark.park)
                        .font(.subheadline)
                    
                    Spacer()
                    
                    Text(landmark.state)
                        .font(.subheadline)
                }
            }
            .padding()
            
            Spacer()
            
        }
    }
}}

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS