diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/BuildingListsAndNavigation.xcodeproj/project.pbxproj b/2 Building Lists and Navigation/StartingPoint/Landmarks/BuildingListsAndNavigation.xcodeproj/project.pbxproj index 4d4bd47..1788611 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/BuildingListsAndNavigation.xcodeproj/project.pbxproj +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/BuildingListsAndNavigation.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 24FEC89922A770D800B58A9D /* LandmarkRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FEC89822A770D800B58A9D /* LandmarkRow.swift */; }; + 24FEC89B22A775AB00B58A9D /* LandmarkList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FEC89A22A775AB00B58A9D /* LandmarkList.swift */; }; B7394866229F194000C47603 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394865229F194000C47603 /* AppDelegate.swift */; }; B7394868229F194000C47603 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394867229F194000C47603 /* SceneDelegate.swift */; }; B739486A229F194000C47603 /* LandmarkDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394869229F194000C47603 /* LandmarkDetail.swift */; }; @@ -33,6 +35,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 24FEC89822A770D800B58A9D /* LandmarkRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandmarkRow.swift; sourceTree = ""; }; + 24FEC89A22A775AB00B58A9D /* LandmarkList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandmarkList.swift; sourceTree = ""; }; B7394862229F194000C47603 /* Landmarks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Landmarks.app; sourceTree = BUILT_PRODUCTS_DIR; }; B7394865229F194000C47603 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B7394867229F194000C47603 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -59,8 +63,8 @@ B739488F229F292F00C47603 /* chincoteague.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = chincoteague.jpg; sourceTree = ""; }; B7394890229F292F00C47603 /* chilkoottrail.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = chilkoottrail.jpg; sourceTree = ""; }; C4E4AAA0C4E4035000000001 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - F4750E80F475342000000001 /* SampleCode.xcconfig */ = {isa = PBXFileReference; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = ""; }; - F479D5A0F479BCB000000001 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE.txt; sourceTree = ""; }; + F4750E80F475342000000001 /* SampleCode.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = ""; }; + F479D5A0F479BCB000000001 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -100,6 +104,8 @@ B7394865229F194000C47603 /* AppDelegate.swift */, B7394867229F194000C47603 /* SceneDelegate.swift */, B7394869229F194000C47603 /* LandmarkDetail.swift */, + 24FEC89822A770D800B58A9D /* LandmarkRow.swift */, + 24FEC89A22A775AB00B58A9D /* LandmarkList.swift */, B739487D229F1C0100C47603 /* Supporting Views */, B7394883229F291A00C47603 /* Resources */, B739486B229F194200C47603 /* Assets.xcassets */, @@ -183,7 +189,6 @@ B739485E229F194000C47603 /* Sources */, B739485F229F194000C47603 /* Frameworks */, B7394860229F194000C47603 /* Resources */, - C602AC70C605937000000001, ); buildRules = ( ); @@ -262,8 +267,10 @@ B7394866229F194000C47603 /* AppDelegate.swift in Sources */, B739487A229F1B3F00C47603 /* CircleImage.swift in Sources */, B739487C229F1B6800C47603 /* MapView.swift in Sources */, + 24FEC89B22A775AB00B58A9D /* LandmarkList.swift in Sources */, B7394868229F194000C47603 /* SceneDelegate.swift in Sources */, B739486A229F194000C47603 /* LandmarkDetail.swift in Sources */, + 24FEC89922A770D800B58A9D /* LandmarkRow.swift in Sources */, B7394881229F28B900C47603 /* Landmark.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift index ee34bba..55477a8 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift @@ -8,24 +8,27 @@ A view showing the details for a landmark. import SwiftUI struct LandmarkDetail: View { + + var landmark: Landmark + var body: some View { VStack { - MapView() + MapView(coordinate: landmark.locationCoordinate) .edgesIgnoringSafeArea(.top) .frame(height: 300) - CircleImage() + CircleImage(image: landmark.image(forSize: 250)) .offset(x: 0, y: -130) .padding(.bottom, -130) VStack(alignment: .leading) { - Text("Turtle Rock") + Text(landmark.name) .font(.title) HStack(alignment: .top) { - Text("Joshua Tree National Park") + Text(landmark.park) .font(.subheadline) Spacer() - Text("California") + Text(landmark.state) .font(.subheadline) } } @@ -39,7 +42,7 @@ struct LandmarkDetail: View { #if DEBUG struct LandmarkDetail_Previews: PreviewProvider { static var previews: some View { - LandmarkDetail() + LandmarkDetail(landmark: landmarkData[0]) } } #endif diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkList.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkList.swift new file mode 100644 index 0000000..30c4c41 --- /dev/null +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkList.swift @@ -0,0 +1,33 @@ +// +// LandmarkList.swift +// Landmarks +// +// Created by Daniel Tull on 04/06/2019. +// Copyright © 2019 Apple. All rights reserved. +// + +import SwiftUI + +struct LandmarkList : View { + var body: some View { + NavigationView { + List(landmarkData) { landmark in + NavigationButton(destination: LandmarkDetail(landmark: landmark)) { + LandmarkRow(landmark: landmark) + } + } + } + .navigationBarTitle(Text("Landmarks")) + } +} + +#if DEBUG +struct LandmarkList_Previews : PreviewProvider { + static var previews: some View { + ForEach(["iPhone SE", "iPhone XS Max"].identified(by: \.self)) { device in + LandmarkList() + .previewDevice(PreviewDevice(rawValue: device)) + } + } +} +#endif diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkRow.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkRow.swift new file mode 100644 index 0000000..bb97cb6 --- /dev/null +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/LandmarkRow.swift @@ -0,0 +1,34 @@ +// +// LandmarkRow.swift +// Landmarks +// +// Created by Daniel Tull on 04/06/2019. +// Copyright © 2019 Apple. All rights reserved. +// + +import SwiftUI + +struct LandmarkRow : View { + + var landmark: Landmark + + var body: some View { + HStack { + landmark.image(forSize: 50) + Text(landmark.name) + Spacer() + } + } +} + +#if DEBUG +struct LandmarkRow_Previews : PreviewProvider { + static var previews: some View { + Group { + LandmarkRow(landmark: landmarkData[0]) + LandmarkRow(landmark: landmarkData[1]) + } + .previewLayout(.fixed(width: 300, height: 70)) + } +} +#endif diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Models/Landmark.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Models/Landmark.swift index 31c85e1..3832625 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Models/Landmark.swift +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Models/Landmark.swift @@ -8,7 +8,7 @@ The model for an individual landmark. import SwiftUI import CoreLocation -struct Landmark: Hashable, Codable { +struct Landmark: Hashable, Identifiable, Codable { var id: Int var name: String fileprivate var imageName: String diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/SceneDelegate.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/SceneDelegate.swift index f7316e2..c4d69a9 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/SceneDelegate.swift +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/SceneDelegate.swift @@ -19,7 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use a UIHostingController as window root view controller let window = UIWindow(frame: UIScreen.main.bounds) - window.rootViewController = UIHostingController(rootView: LandmarkDetail()) + window.rootViewController = UIHostingController(rootView: LandmarkList()) self.window = window window.makeKeyAndVisible() } diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/CircleImage.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/CircleImage.swift index f32a333..98bd278 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/CircleImage.swift +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/CircleImage.swift @@ -8,8 +8,11 @@ A view that clips an image to a circle and adds a stroke and shadow. import SwiftUI struct CircleImage: View { + + var image: Image + var body: some View { - Image("turtlerock") + image .clipShape(Circle()) .overlay( Circle().stroke(Color.white, lineWidth: 4)) @@ -20,7 +23,7 @@ struct CircleImage: View { #if DEBUG struct CircleImage_Previews: PreviewProvider { static var previews: some View { - CircleImage() + CircleImage(image: Image("turtlerock")) } } #endif diff --git a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/MapView.swift b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/MapView.swift index ea5716e..953d5ce 100644 --- a/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/MapView.swift +++ b/2 Building Lists and Navigation/StartingPoint/Landmarks/Landmarks/Supporting Views/MapView.swift @@ -9,13 +9,14 @@ import SwiftUI import MapKit struct MapView: UIViewRepresentable { + + var coordinate: CLLocationCoordinate2D + func makeUIView(context: Context) -> MKMapView { MKMapView(frame: .zero) } func updateUIView(_ view: MKMapView, context: Context) { - let coordinate = CLLocationCoordinate2D( - latitude: 34.011_286, longitude: -116.166_868) let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0) let region = MKCoordinateRegion(center: coordinate, span: span) view.setRegion(region, animated: true) @@ -25,7 +26,8 @@ struct MapView: UIViewRepresentable { #if DEBUG struct MapView_Previews: PreviewProvider { static var previews: some View { - MapView() + MapView(coordinate: CLLocationCoordinate2D( + latitude: 34.011_286, longitude: -116.166_868)) } } #endif diff --git a/3 Handling User Input/StartingPoint/Landmarks/HandlingUserInput.xcodeproj/project.pbxproj b/3 Handling User Input/StartingPoint/Landmarks/HandlingUserInput.xcodeproj/project.pbxproj index 60294c7..bd382f2 100644 --- a/3 Handling User Input/StartingPoint/Landmarks/HandlingUserInput.xcodeproj/project.pbxproj +++ b/3 Handling User Input/StartingPoint/Landmarks/HandlingUserInput.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24FEC89D22A7813C00B58A9D /* UserData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FEC89C22A7813C00B58A9D /* UserData.swift */; }; B7394866229F194000C47603 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394865229F194000C47603 /* AppDelegate.swift */; }; B7394868229F194000C47603 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394867229F194000C47603 /* SceneDelegate.swift */; }; B739486A229F194000C47603 /* LandmarkDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7394869229F194000C47603 /* LandmarkDetail.swift */; }; @@ -35,8 +36,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 8679DA608679DDE000000001 /* SampleCode.xcconfig */ = {isa = PBXFileReference; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = ""; }; - 867DE190867CBB1000000001 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE.txt; sourceTree = ""; }; + 24FEC89C22A7813C00B58A9D /* UserData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserData.swift; sourceTree = ""; }; + 8679DA608679DDE000000001 /* SampleCode.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = ""; }; + 867DE190867CBB1000000001 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; B7394862229F194000C47603 /* Landmarks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Landmarks.app; sourceTree = BUILT_PRODUCTS_DIR; }; B7394865229F194000C47603 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B7394867229F194000C47603 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -123,6 +125,7 @@ B73948A0229F2E1F00C47603 /* LandmarkList.swift */, B739489E229F2D9700C47603 /* LandmarkRow.swift */, B7394869229F194000C47603 /* LandmarkDetail.swift */, + 24FEC89C22A7813C00B58A9D /* UserData.swift */, B739487D229F1C0100C47603 /* Supporting Views */, B7394883229F291A00C47603 /* Resources */, B739486B229F194200C47603 /* Assets.xcassets */, @@ -189,7 +192,6 @@ B739485E229F194000C47603 /* Sources */, B739485F229F194000C47603 /* Frameworks */, B7394860229F194000C47603 /* Resources */, - C602AC70C605937000000001, ); buildRules = ( ); @@ -272,6 +274,7 @@ B7394868229F194000C47603 /* SceneDelegate.swift in Sources */, B739486A229F194000C47603 /* LandmarkDetail.swift in Sources */, B739489F229F2D9700C47603 /* LandmarkRow.swift in Sources */, + 24FEC89D22A7813C00B58A9D /* UserData.swift in Sources */, B7394881229F28B900C47603 /* Landmark.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift index e957172..e5356ca 100644 --- a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift +++ b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkDetail.swift @@ -8,7 +8,11 @@ A view showing the details for a landmark. import SwiftUI struct LandmarkDetail: View { + @EnvironmentObject var userData: UserData var landmark: Landmark + var landmarkIndex: Int { + userData.landmarks.firstIndex(where: { $0.id == landmark.id })! + } var body: some View { VStack { @@ -20,8 +24,22 @@ struct LandmarkDetail: View { .padding(.bottom, -130) VStack(alignment: .leading) { - Text(landmark.name) - .font(.title) + HStack { + Text(landmark.name) + .font(.title) + + Button(action: { + self.userData.landmarks[self.landmarkIndex].isFavorite.toggle() + }) { + if self.userData.landmarks[self.landmarkIndex].isFavorite { + Image(systemName: "star.fill") + .foregroundColor(.yellow) + } else { + Image(systemName: "star") + .foregroundColor(.gray) + } + } + } HStack(alignment: .top) { Text(landmark.park) @@ -43,6 +61,7 @@ struct LandmarkDetail: View { struct LandmarkDetail_Previews: PreviewProvider { static var previews: some View { LandmarkDetail(landmark: landmarkData[0]) + .environmentObject(UserData()) } } #endif diff --git a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkList.swift b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkList.swift index 1c5b7ee..9ca5fa6 100644 --- a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkList.swift +++ b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/LandmarkList.swift @@ -8,11 +8,21 @@ A view showing a list of landmarks. import SwiftUI struct LandmarkList: View { + + @EnvironmentObject var userData: UserData + var body: some View { NavigationView { - List(landmarkData) { landmark in - NavigationButton(destination: LandmarkDetail(landmark: landmark)) { - LandmarkRow(landmark: landmark) + List { + Toggle(isOn: $userData.showFavoritesOnly) { + Text("Show Favorites Only") + } + ForEach(self.userData.landmarks) { landmark in + if !self.userData.showFavoritesOnly || landmark.isFavorite { + NavigationButton(destination: LandmarkDetail(landmark: landmark)) { + LandmarkRow(landmark: landmark) + } + } } } .navigationBarTitle(Text("Landmarks"), displayMode: .large) @@ -23,11 +33,8 @@ struct LandmarkList: View { #if DEBUG struct LandmarkList_Previews: PreviewProvider { static var previews: some View { - ForEach(["iPhone SE", "iPhone XS Max"].identified(by: \.self)) { deviceName in - LandmarkList() - .previewDevice(PreviewDevice(rawValue: deviceName)) - .previewDisplayName(deviceName) - } + LandmarkList() + .environmentObject(UserData()) } } #endif diff --git a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/Supporting Views/LandmarkRow.swift b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/Supporting Views/LandmarkRow.swift index e90259e..ebeb7e9 100644 --- a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/Supporting Views/LandmarkRow.swift +++ b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/Supporting Views/LandmarkRow.swift @@ -14,6 +14,12 @@ struct LandmarkRow: View { HStack { landmark.image(forSize: 50) Text(landmark.name) + Spacer() + if landmark.isFavorite { + Image(systemName: "star.fill") + .imageScale(.large) + .foregroundColor(.yellow) + } } } } diff --git a/3 Handling User Input/StartingPoint/Landmarks/Landmarks/UserData.swift b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/UserData.swift new file mode 100644 index 0000000..9f4615e --- /dev/null +++ b/3 Handling User Input/StartingPoint/Landmarks/Landmarks/UserData.swift @@ -0,0 +1,16 @@ + +import Combine +import SwiftUI + +final class UserData: BindableObject { + + let didChange = PassthroughSubject() + + var showFavoritesOnly = false { + didSet { didChange.send(self) } + } + + var landmarks = landmarkData { + didSet { didChange.send(self) } + } +}