|
6 | 6 | // |
7 | 7 |
|
8 | 8 | import SwiftUI |
| 9 | +import MapKit |
9 | 10 |
|
10 | 11 | struct DirectionsView: View { |
| 12 | + @StateObject private var directionsManager = DirectionsManager.shared |
| 13 | + @ObservedObject private var locationManager = LocationManager.shared |
| 14 | + @ObservedObject private var mapSearch = MapSearch() |
| 15 | + |
| 16 | + @FocusState private var isSearching: Bool |
| 17 | + // TODO: check |
| 18 | + @State private var region = MKCoordinateRegion( |
| 19 | + center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), |
| 20 | + span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05) |
| 21 | + ) |
| 22 | + |
11 | 23 | var body: some View { |
12 | | - List { |
13 | | - |
| 24 | + VStack { |
| 25 | + if locationManager.canGetUserLocation() { |
| 26 | + VStack { |
| 27 | + if directionsManager.isLoading || mapSearch.isLoading { |
| 28 | + ProgressView("Loading...") |
| 29 | + } else { |
| 30 | + List { |
| 31 | + Section { |
| 32 | + ForEach(mapSearch.locationResults, id: \.description) { location in |
| 33 | + Button { |
| 34 | + directionsManager.cancelRoute() |
| 35 | + locationManager.getCoordinateFrom(address: "\(location.title), \(location.subtitle)") { coordinate, error in |
| 36 | + if let coordinate { |
| 37 | + directionsManager.getDirections(to: coordinate) |
| 38 | + mapSearch.locationResults = [] |
| 39 | + } else if let error { |
| 40 | + print("Error getting coordinate: \(error.localizedDescription)") |
| 41 | + } |
| 42 | + } |
| 43 | + } label: { |
| 44 | + VStack(alignment: .leading, spacing: 4) { |
| 45 | + Text(location.title) |
| 46 | + .foregroundStyle(Color.primary) |
| 47 | + .fontWeight(.semibold) |
| 48 | + if !location.subtitle.isEmpty { |
| 49 | + Text(location.subtitle) |
| 50 | + .foregroundStyle(.gray) |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + } |
| 55 | + } |
| 56 | + directions |
| 57 | + mapView |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + .searchable(text: $mapSearch.searchTerm) |
| 62 | + } else { |
| 63 | + Text("To start a route, you need to enable \"Always Allow\" location permissions for InfiniLink in Settings.") |
| 64 | + } |
| 65 | + } |
| 66 | + .navigationTitle("Directions") |
| 67 | + .onChange(of: locationManager.location) { location in |
| 68 | + if let location { |
| 69 | + directionsManager.updateLocation(location) |
| 70 | + region.center = location.coordinate |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + var directions: some View { |
| 76 | + Group { |
| 77 | + if !directionsManager.steps.isEmpty { |
| 78 | + Section { |
| 79 | + VStack { |
| 80 | + let instructions = directionsManager.steps[directionsManager.currentStepIndex].instructions |
| 81 | + if !instructions.isEmpty { |
| 82 | + Text(instructions) |
| 83 | + .font(.title2.weight(.bold)) |
| 84 | + Text("In \(directionsManager.distanceToNextStep) meters") |
| 85 | + } else { |
| 86 | + ProgressView() |
| 87 | + } |
| 88 | + } |
| 89 | + .frame(maxWidth: .infinity) |
| 90 | + .padding() |
| 91 | + } |
| 92 | + } |
| 93 | + Section { |
| 94 | + ForEach(directionsManager.steps, id: \.instructions) { step in |
| 95 | + VStack(alignment: .leading, spacing: 4) { |
| 96 | + Text(step.instructions) |
| 97 | + .foregroundStyle(Color.primary) |
| 98 | + .fontWeight(.semibold) |
| 99 | + Text(directionsManager.convertedDistance(step.distance)) |
| 100 | + .foregroundStyle(.gray) |
| 101 | + } |
| 102 | + } |
| 103 | + } |
14 | 104 | } |
15 | | - .navigationTitle("Navigation") |
| 105 | + } |
| 106 | + |
| 107 | + var mapView: some View { |
| 108 | + Section { |
| 109 | + Map(coordinateRegion: $region, showsUserLocation: true, userTrackingMode: .constant(.follow)) |
| 110 | + .frame(height: 400) |
| 111 | + } |
| 112 | + .listRowInsets(EdgeInsets()) |
16 | 113 | } |
17 | 114 | } |
18 | 115 |
|
19 | 116 | #Preview { |
20 | | - DirectionsView() |
| 117 | + NavigationStack { |
| 118 | + DirectionsView() |
| 119 | + .navigationBarTitleDisplayMode(.inline) |
| 120 | + } |
21 | 121 | } |
0 commit comments