#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() } } }}