refactor(UIKit): add search sheet to uikit
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							| @@ -15,14 +15,6 @@ struct ContentView: View { | ||||
|     @StateObject var locationManager = LocationManager() | ||||
|     @StateObject var healthKitManager = HealthKitManager() | ||||
|      | ||||
|     @State private var position: MapCameraPosition = .automatic | ||||
|     @State private var showSearch: Bool = true | ||||
|     @State private var directions: [MKRoute] = [] | ||||
|     @State private var destination: MKMapItem? | ||||
|  | ||||
|      | ||||
|     @State var healthKitAccess = false | ||||
|     @State var stepLength: Double? | ||||
|  | ||||
|     // TODO: create a map | ||||
|     // Add navigation to the map | ||||
| @@ -42,60 +34,24 @@ struct ContentView: View { | ||||
|     // calculate only the distance between the start and end instead of getting directions for everything | ||||
|     // if user clicks on the place, display better view and then calculate route there | ||||
|     var body: some View { | ||||
|         ZStack { | ||||
|         MapView(locationManager: locationManager) | ||||
| //        Map(position: $position) { | ||||
| //            UserAnnotation() | ||||
| //            ForEach(0..<directions.count) { i in | ||||
| //                if destination != nil { | ||||
| //                    Marker(item: destination!) | ||||
| //                } | ||||
| //                MapPolyline(directions[i].polyline) | ||||
| //                    .stroke(Defaults.routeColor[i], lineWidth: Defaults.routeWidth) | ||||
| //            } | ||||
|         } | ||||
|         .ignoresSafeArea() | ||||
|         .sheet( | ||||
|             isPresented: $showSearch, | ||||
|             content: { | ||||
|                 SearchView( | ||||
|                     directions: $directions, stepLength: $stepLength, | ||||
|                     locationManager: locationManager, destination: $destination | ||||
|                 ) | ||||
|                 .ignoresSafeArea() | ||||
|             } | ||||
|         ) | ||||
|         .mapControls { | ||||
|             // TODO: make sure the user location stays on the map even if camera moves | ||||
|             MapUserLocationButton() | ||||
|                 .onTapGesture { | ||||
|                     zoomUserLocation() | ||||
|         MapView(locationManager: locationManager, viewModel: viewModel) | ||||
|         //        Map(position: $position) { | ||||
|         //            UserAnnotation() | ||||
|         //            ForEach(0..<directions.count) { i in | ||||
|         //                if destination != nil { | ||||
|         //                    Marker(item: destination!) | ||||
|         //                } | ||||
|         //                MapPolyline(directions[i].polyline) | ||||
|         //                    .stroke(Defaults.routeColor[i], lineWidth: Defaults.routeWidth) | ||||
|         //            } | ||||
|             .ignoresSafeArea() | ||||
|             .onAppear { | ||||
|                 Task { | ||||
|                     await healthKitManager.requestAccess() | ||||
|                     viewModel.stepLength = await healthKitManager.getStepLength() | ||||
|                 } | ||||
|             MapCompass() | ||||
|         } | ||||
|         .onAppear { | ||||
|             zoomUserLocation() | ||||
|             Task { | ||||
|                 await healthKitManager.requestAccess() | ||||
|                 stepLength = await healthKitManager.getStepLength() | ||||
|             } | ||||
|         } | ||||
|         .onChange(of: locationManager.location) { | ||||
|             zoomUserLocation() | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     func zoomUserLocation() { | ||||
|         withAnimation { | ||||
|             locationManager.requestAuthorization() | ||||
|             locationManager.requestLocation() | ||||
|             if let userLocation = locationManager.location { | ||||
|                 position = .camera(MapCamera(centerCoordinate: userLocation, distance: 1000)) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|      | ||||
|     func getLastPointFor(route: MKRoute) -> CLLocationCoordinate2D? { | ||||
|         let pointCount =  route.polyline.pointCount | ||||
|         if pointCount > 0 { | ||||
|   | ||||
| @@ -13,20 +13,20 @@ import SwiftUI | ||||
| struct SearchItemView: View { | ||||
|     var location: MKMapItem | ||||
|     @State var distance: CLLocationDistance? | ||||
|     @Binding var directions: [MKRoute] | ||||
|     @Binding var stepLength: Double? | ||||
|     @Binding var showSteps: Bool | ||||
|     @State var localDirections: [MKRoute] = [] | ||||
|     @Binding var destination: MKMapItem? | ||||
|     @ObservedObject var viewModel: ViewModel | ||||
|  | ||||
|     var body: some View { | ||||
|  | ||||
|         Button( | ||||
|             action: { | ||||
|                 if localDirections == [] { | ||||
|                     print("finding directions") | ||||
|                     findDirections() | ||||
|                 } | ||||
|                 directions = localDirections | ||||
|                 viewModel.directions = localDirections | ||||
|                 print("Directions set") | ||||
|             }, | ||||
|             label: { | ||||
|                 HStack { | ||||
| @@ -86,7 +86,7 @@ struct SearchItemView: View { | ||||
|     } | ||||
|  | ||||
|     func formatDistance(distance: CLLocationDistance) -> String { | ||||
|         let steps = distance / (stepLength ?? 1) | ||||
|         let steps = distance / (viewModel.stepLength ?? 1) | ||||
|         if steps != 0 && showSteps { | ||||
|             let formatter = NumberFormatter() | ||||
|             formatter.maximumFractionDigits = 0 | ||||
| @@ -118,7 +118,7 @@ struct SearchItemView: View { | ||||
|             } | ||||
|             self.localDirections = response.routes | ||||
|             self.distance = response.routes.first?.distance | ||||
|             self.destination = location | ||||
|             viewModel.destination = location | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,56 +11,54 @@ import SwiftUI | ||||
| struct SearchView: View { | ||||
|     @State private var query: String = "" | ||||
|     @State private var locations: [MKMapItem] = [] | ||||
|     @Binding var directions: [MKRoute] | ||||
|     @Binding var stepLength: Double? | ||||
|     @State var showSteps = true | ||||
|     var locationManager: LocationManager | ||||
|     @Binding var destination: MKMapItem? | ||||
|     @ObservedObject var viewModel: ViewModel | ||||
|  | ||||
|     var body: some View { | ||||
|         VStack { | ||||
|             HStack { | ||||
|                 Image(systemName: "magnifyingglass") | ||||
|                 TextField("Search for any location", text: $query) | ||||
|                     .autocorrectionDisabled() | ||||
|                     .onChange(of: self.query) { | ||||
|                         if query.count > 0 { | ||||
|                             search(for: self.query) | ||||
|                         } else { | ||||
|                             self.locations = [] | ||||
|         ZStack { | ||||
|             Rectangle() | ||||
|                 .fill(.thinMaterial) | ||||
|                 .ignoresSafeArea() | ||||
|             VStack { | ||||
|                 HStack { | ||||
|                     Image(systemName: "magnifyingglass") | ||||
|                     TextField("Search for any location", text: $query) | ||||
|                         .autocorrectionDisabled() | ||||
|                         .onChange(of: self.query) { | ||||
|                             if query.count > 0 { | ||||
|                                 search(for: self.query) | ||||
|                             } else { | ||||
|                                 self.locations = [] | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     //                                        .onAppear { | ||||
|                     //                                            // TODO: delete this, it's for debug only | ||||
|                     //                                            search(for: self.query) | ||||
|                     //                                        } | ||||
|                     .overlay { | ||||
|                         HStack { | ||||
|                             Spacer() | ||||
|                             Image(systemName: "multiply.circle.fill") | ||||
|                                 .foregroundStyle(.gray) | ||||
|                                 .onTapGesture { | ||||
|                                     query = "" | ||||
|                                 } | ||||
|                         .overlay { | ||||
|                             HStack { | ||||
|                                 Spacer() | ||||
|                                 Image(systemName: "multiply.circle.fill") | ||||
|                                     .foregroundStyle(.gray) | ||||
|                                     .onTapGesture { | ||||
|                                         query = "" | ||||
|                                     } | ||||
|                             } | ||||
|                         } | ||||
|                 } | ||||
|                 .modifier(TextFieldGrayBackgroudColor()) | ||||
|                 Spacer() | ||||
|                 ScrollView { | ||||
|                     ForEach(self.locations, id: \.identifier) { location in | ||||
|                         SearchItemView(location: location, showSteps: $showSteps, viewModel: viewModel) | ||||
|                     } | ||||
|             } | ||||
|             .modifier(TextFieldGrayBackgroudColor()) | ||||
|             Spacer() | ||||
|             ScrollView { | ||||
|                 ForEach(self.locations, id: \.identifier) { location in | ||||
|                     SearchItemView( | ||||
|                         location: location, directions: $directions, stepLength: $stepLength, | ||||
|                         showSteps: $showSteps, destination: $destination) | ||||
|                 } | ||||
|             } | ||||
|             .padding() | ||||
|             .interactiveDismissDisabled() | ||||
|             .ignoresSafeArea() | ||||
|         } | ||||
|         .padding() | ||||
|         .interactiveDismissDisabled() | ||||
|  | ||||
|         .presentationDetents([.height(200), .large]) | ||||
|         .presentationBackground(.regularMaterial) | ||||
|         .presentationBackgroundInteraction(.enabled(upThrough: .large)) | ||||
|     } | ||||
|  | ||||
|     func search(for text: String) { | ||||
|   | ||||
| @@ -11,6 +11,9 @@ import SwiftUI | ||||
|  | ||||
| class UIKitMapView: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate { | ||||
|     var locationManager: LocationManager | ||||
|     var viewModel: ViewModel | ||||
|     var directions: [MKRoute] = [] | ||||
|     var destination: MKMapItem? | ||||
|     let mapView : MKMapView = { | ||||
|         let map = MKMapView() | ||||
|         map.showsUserTrackingButton = true | ||||
| @@ -18,8 +21,10 @@ class UIKitMapView: UIViewController, MKMapViewDelegate, CLLocationManagerDelega | ||||
|         return map | ||||
|     }() | ||||
|      | ||||
|     init(locationManager: LocationManager) { | ||||
|      | ||||
|     init(locationManager: LocationManager, viewModel: ViewModel) { | ||||
|         self.locationManager = locationManager | ||||
|         self.viewModel = viewModel | ||||
|         super.init(nibName: nil, bundle: nil) | ||||
|     } | ||||
|      | ||||
| @@ -35,6 +40,10 @@ class UIKitMapView: UIViewController, MKMapViewDelegate, CLLocationManagerDelega | ||||
|         setLocation() | ||||
|     } | ||||
|      | ||||
|     override func viewDidAppear(_ animated: Bool) { | ||||
|         setSearchView() | ||||
|     } | ||||
|      | ||||
|     private func setLocation() { | ||||
|         locationManager.requestAuthorization() | ||||
|         locationManager.requestLocation() | ||||
| @@ -54,14 +63,35 @@ class UIKitMapView: UIViewController, MKMapViewDelegate, CLLocationManagerDelega | ||||
|         mapView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true | ||||
|     } | ||||
|      | ||||
|     private func setSearchView() { | ||||
|         let searchViewConctroller = UIHostingController(rootView: SearchView(locationManager: locationManager, viewModel: viewModel)) | ||||
|         searchViewConctroller.view.backgroundColor = .clear | ||||
|         searchViewConctroller.modalPresentationStyle = .pageSheet | ||||
|         searchViewConctroller.edgesForExtendedLayout = [.top, .bottom, .left, .right] | ||||
|         if let sheet = searchViewConctroller.sheetPresentationController { | ||||
|             let smallDetentId = UISheetPresentationController.Detent.Identifier("small") | ||||
|             let smallDetent = UISheetPresentationController.Detent.custom(identifier: smallDetentId) { context in | ||||
|                 return 200 | ||||
|             } | ||||
|             sheet.detents = [smallDetent, .large()] | ||||
|             sheet.largestUndimmedDetentIdentifier = .large | ||||
|             sheet.prefersScrollingExpandsWhenScrolledToEdge = false | ||||
|             sheet.prefersGrabberVisible = true | ||||
|             sheet.prefersEdgeAttachedInCompactHeight = true | ||||
|             sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true | ||||
|         } | ||||
|         self.present(searchViewConctroller, animated: true, completion: nil) | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| struct MapView: UIViewControllerRepresentable { | ||||
|     typealias UIViewControllerType = UIKitMapView | ||||
|     @StateObject var locationManager: LocationManager | ||||
|     @ObservedObject var viewModel: ViewModel | ||||
|  | ||||
|     func makeUIViewController(context: Context) -> UIKitMapView { | ||||
|         return UIKitMapView(locationManager: locationManager) | ||||
|         return UIKitMapView(locationManager: locationManager, viewModel: viewModel) | ||||
|     } | ||||
|      | ||||
|     func updateUIViewController(_ uiViewController: UIKitMapView, context: Context) { | ||||
|   | ||||
| @@ -6,9 +6,13 @@ | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
| import MapKit | ||||
|  | ||||
| class ViewModel: ObservableObject { | ||||
|     @Published var test: String = UserDefaults.standard.string(forKey: "test") ?? "" | ||||
|     @Published var directions: [MKRoute] = [] | ||||
|     @Published var stepLength: Double? | ||||
|     @Published var destination: MKMapItem? | ||||
|  | ||||
|     func saveValue(_ value: String) { | ||||
|         UserDefaults.standard.set(value, forKey: "test") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Oliver
					Oliver