From 132969d847a4d418876122522229227c01294075 Mon Sep 17 00:00:00 2001 From: John O'Reilly Date: Sat, 27 Feb 2021 10:11:41 +0000 Subject: [PATCH] SwiftUI: show iss position on map --- .../project.pbxproj | 4 ++ .../PeopleInSpaceSwiftUI/ContentView.swift | 47 ++++++++++++------- .../PeopleInSpaceSwiftUI/ISSMapView.swift | 44 +++++++++++++++++ .../PeopleInSpaceSwiftUI/ViewModel.swift | 6 +-- 4 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ISSMapView.swift diff --git a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI.xcodeproj/project.pbxproj b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI.xcodeproj/project.pbxproj index 1a9ebb0..f05c804 100644 --- a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI.xcodeproj/project.pbxproj +++ b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 1ABFB8C723AFF5CE003D807E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ABFB8C623AFF5CE003D807E /* Preview Assets.xcassets */; }; 1ABFB8CA23AFF5CE003D807E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABFB8C823AFF5CE003D807E /* LaunchScreen.storyboard */; }; 1AC2439025A1D57700F17D2F /* IssPositionPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */; }; + 1AD2EC6A25E9984900CCEE81 /* ISSMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD2EC6925E9984900CCEE81 /* ISSMapView.swift */; }; 1E1255057DE614781855FC02 /* libPods-PeopleInSpaceSwiftUI.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28681F6577A67864E2C2D9B4 /* libPods-PeopleInSpaceSwiftUI.a */; }; /* End PBXBuildFile section */ @@ -35,6 +36,7 @@ 1ABFB8C923AFF5CE003D807E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 1ABFB8CB23AFF5CE003D807E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssPositionPublisher.swift; sourceTree = ""; }; + 1AD2EC6925E9984900CCEE81 /* ISSMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ISSMapView.swift; sourceTree = ""; }; 28681F6577A67864E2C2D9B4 /* libPods-PeopleInSpaceSwiftUI.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PeopleInSpaceSwiftUI.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 56CD7101BBBC60F561BFB049 /* Pods-PeopleInSpaceSwiftUI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PeopleInSpaceSwiftUI.release.xcconfig"; path = "Target Support Files/Pods-PeopleInSpaceSwiftUI/Pods-PeopleInSpaceSwiftUI.release.xcconfig"; sourceTree = ""; }; E9A29AFE5FFFB564C509840A /* Pods-PeopleInSpaceSwiftUI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PeopleInSpaceSwiftUI.debug.xcconfig"; path = "Target Support Files/Pods-PeopleInSpaceSwiftUI/Pods-PeopleInSpaceSwiftUI.debug.xcconfig"; sourceTree = ""; }; @@ -102,6 +104,7 @@ 1ABD44F923B00008008387E3 /* ViewModel.swift */, 1AA82EEC25052E1E00193051 /* ImageView.swift */, 1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */, + 1AD2EC6925E9984900CCEE81 /* ISSMapView.swift */, ); path = PeopleInSpaceSwiftUI; sourceTree = ""; @@ -225,6 +228,7 @@ 1AC2439025A1D57700F17D2F /* IssPositionPublisher.swift in Sources */, 1ABFB8BE23AFF5CC003D807E /* AppDelegate.swift in Sources */, 1ABFB8C023AFF5CC003D807E /* SceneDelegate.swift in Sources */, + 1AD2EC6A25E9984900CCEE81 /* ISSMapView.swift in Sources */, 1ABD44FA23B00008008387E3 /* ViewModel.swift in Sources */, 1ABFB8C223AFF5CC003D807E /* ContentView.swift in Sources */, ); diff --git a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ContentView.swift b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ContentView.swift index 6c82d8b..ef4e58b 100644 --- a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ContentView.swift +++ b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ContentView.swift @@ -1,29 +1,42 @@ import SwiftUI +import Combine import common struct ContentView: View { - @StateObject var peopleInSpaceViewModel = PeopleInSpaceViewModel(repository: PeopleInSpaceRepository()) + @StateObject var viewModel = PeopleInSpaceViewModel(repository: PeopleInSpaceRepository()) + + var body: some View { + TabView { + PeopleListView(viewModel: viewModel) + .tabItem { + Label("People", systemImage: "person") + } + ISSMapView(issPosition: $viewModel.issPosition) + .tabItem { + Label("Map", systemImage: "location") + } + } + } +} + +struct PeopleListView: View { + @ObservedObject var viewModel: PeopleInSpaceViewModel var body: some View { NavigationView { - VStack { - HStack { - Text(peopleInSpaceViewModel.issPositionString) - } - .padding(EdgeInsets(top: 18, leading: 16, bottom: 0, trailing: 16)) - + VStack { - List(peopleInSpaceViewModel.people, id: \.name) { person in - NavigationLink(destination: PersonDetailsView(peopleInSpaceViewModel: peopleInSpaceViewModel, person: person)) { - PersonView(peopleInSpaceViewModel: self.peopleInSpaceViewModel, person: person) + List(viewModel.people, id: \.name) { person in + NavigationLink(destination: PersonDetailsView(viewModel: viewModel, person: person)) { + PersonView(viewModel: viewModel, person: person) } } .navigationBarTitle(Text("People In Space")) .onAppear { - self.peopleInSpaceViewModel.startObservingPeopleUpdates() + viewModel.startObservingPeopleUpdates() }.onDisappear { - self.peopleInSpaceViewModel.stopObservingPeopleUpdates() + viewModel.stopObservingPeopleUpdates() } } } @@ -31,12 +44,12 @@ struct ContentView: View { } struct PersonView: View { - var peopleInSpaceViewModel: PeopleInSpaceViewModel + var viewModel: PeopleInSpaceViewModel var person: Assignment var body: some View { HStack { - ImageView(withURL: peopleInSpaceViewModel.getPersonImage(personName: person.name), width: 64, height: 64) + ImageView(withURL: viewModel.getPersonImage(personName: person.name), width: 64, height: 64) VStack(alignment: .leading) { Text(person.name).font(.headline) Text(person.craft).font(.subheadline) @@ -47,7 +60,7 @@ struct PersonView: View { struct PersonDetailsView: View { - var peopleInSpaceViewModel: PeopleInSpaceViewModel + var viewModel: PeopleInSpaceViewModel var person: Assignment var body: some View { @@ -55,9 +68,9 @@ struct PersonDetailsView: View { VStack(alignment: .center, spacing: 32) { Text(person.name).font(.title) - ImageView(withURL: peopleInSpaceViewModel.getPersonImage(personName: person.name), width: 240, height: 240) + ImageView(withURL: viewModel.getPersonImage(personName: person.name), width: 240, height: 240) - Text(peopleInSpaceViewModel.getPersonBio(personName: person.name)).font(.body) + Text(viewModel.getPersonBio(personName: person.name)).font(.body) Spacer() } .padding() diff --git a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ISSMapView.swift b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ISSMapView.swift new file mode 100644 index 0000000..16ec96b --- /dev/null +++ b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ISSMapView.swift @@ -0,0 +1,44 @@ +import Foundation +import SwiftUI +import MapKit +import common + + +struct ISSMapView: View { + @Binding var issPosition: IssPosition + + var body: some View { + let issCoordinatePosition = CLLocationCoordinate2D(latitude: issPosition.latitude, longitude: issPosition.longitude) + let regionBinding = Binding( + get: { + MKCoordinateRegion(center: issCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 150, longitudeDelta: 150)) + }, + set: { _ in } + ) + VStack { + Text("\(issPosition.latitude), \(issPosition.longitude)") + Map(coordinateRegion: regionBinding, showsUserLocation: true, + annotationItems: [ Location(coordinate: issCoordinatePosition) ]) { (location) -> MapPin in + MapPin(coordinate: location.coordinate) + } + } + } +} + + +struct Location: Identifiable { + let id = UUID() + let coordinate: CLLocationCoordinate2D +} + +struct MapView: View { + let issPosition: IssPosition + let region: Binding + + var body: some View { + Map(coordinateRegion: region, showsUserLocation: true, + annotationItems: [ Location(coordinate: .init(latitude: issPosition.latitude, longitude: issPosition.longitude)) ]) { (location) -> MapPin in + MapPin(coordinate: location.coordinate) + } + } +} diff --git a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ViewModel.swift b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ViewModel.swift index d6abe94..695ca4e 100644 --- a/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ViewModel.swift +++ b/ios/PeopleInSpaceSwiftUI/PeopleInSpaceSwiftUI/ViewModel.swift @@ -5,7 +5,7 @@ import common class PeopleInSpaceViewModel: ObservableObject { @Published var people = [Assignment]() - @Published var issPositionString = "" + @Published var issPosition = IssPosition(latitude: 0, longitude: 0) private var subscription: AnyCancellable? @@ -14,8 +14,8 @@ class PeopleInSpaceViewModel: ObservableObject { self.repository = repository subscription = IssPositionPublisher(repository: repository) - .map { position in String(format: "ISS Position = (%f, %f)", position.latitude, position.longitude ) } - .assign(to: \.issPositionString, on: self) + //.map { position in String(format: "ISS Position = (%f, %f)", position.latitude, position.longitude ) } + .assign(to: \.issPosition, on: self) } func startObservingPeopleUpdates() {