struct LocationRequestIsOnView: View { @State private var selectedTabBarButton = "" @Environment(\.requestReview) var requestReview @Binding var cameraPosition: MapCameraPosition @Binding var searchResults: [MKMapItem] @Binding var selectedResult: MKMapItem? @Binding var route: MKRoute? @Binding var visibleRegion: MKCoordinateRegion? @Binding var defaultSearchText: String @Binding var isLocationDeniedViewShown: Bool @Binding var isShowingBottomSheet: Bool @StateObject var locationManager = LocationManager() @StateObject var inAppPurchaseManager = InAppPurchaseManager() @EnvironmentObject var favoriteChargingPointSelection: FavoriteChargingPointSelection private let onboardingTip = OnboardingTip() var body: some View { NavigationStack { mapView .popoverTip(onboardingTip, arrowEdge: .top) .mapStyle(.standard(elevation: .realistic)) .safeAreaInset(edge: .bottom) { NavigationLink(destination: SettingsView(isShowingBottomSheet: $isShowingBottomSheet)) { poiButtons .background(.thinMaterial) } } .sheet(isPresented: $isShowingBottomSheet) { if let selectedResult { ItemInfoView( isShowingBottomSheet: $isShowingBottomSheet, route: $route, selectedTabBarButton: selectedTabBarButton, selectedResult: selectedResult, url: self.shareLocation() ) .presentationDetents([.medium, .fraction(0.12), .large]) .presentationBackgroundInteraction(.enabled(upThrough: .medium)) } } .onAppear(perform: { ReviewHandler.requestReview() }) .onChange(of: searchResults) { if !searchResults.isEmpty { let results = searchResults.map { $0.placemark.coordinate } visibleRegion = calculateRegionToFit(coordinates: results) cameraPosition = .region(visibleRegion ?? MKCoordinateRegion()) } } .onChange(of: selectedResult) { if let selectedResult { getDirections() if selectedResult.pointOfInterestCategory == .evCharger, let userLocation = locationManager.userLocation { let region = MKCoordinateRegion(center: userLocation, span: MKCoordinateSpan(latitudeDelta: 0.0291, longitudeDelta: 0.060)) visibleRegion = region cameraPosition = .region(visibleRegion ?? MKCoordinateRegion()) selectedTabBarButton = "EV" } else { if let userLocation = locationManager.userLocation { let region = MKCoordinateRegion(center: userLocation, span: MKCoordinateSpan(latitudeDelta:0.0291, longitudeDelta: 0.060)) visibleRegion = region cameraPosition = .region(visibleRegion ?? MKCoordinateRegion()) selectedTabBarButton = "Gas" } } } } .onMapCameraChange { context in visibleRegion = context.region } .mapControls { MapUserLocationButton() MapCompass() MapScaleView() } } } var mapView: some View { Map(position: $cameraPosition, selection: $selectedResult) { ForEach(searchResults, id:\.self) { Marker(item: $0) } .annotationTitles(.hidden) UserAnnotation() if let route { MapPolyline(route) .stroke(.blue, lineWidth: 5) } } } var poiButtons: some View { HStack { Spacer() VStack(spacing: 0) { POIButtons( searchResults: $searchResults, selectedTabBarButton: $selectedTabBarButton, visibleRegion: $visibleRegion, route: $route, isShowingBottomSheet: $isShowingBottomSheet, defaultSearchText: $defaultSearchText ) .padding(.top) } Spacer() } } } extension LocationRequestIsOnView { func calculateRegionToFit(coordinates: [CLLocationCoordinate2D]) -> MKCoordinateRegion? { // First check if the coordinates array is empty guard !coordinates.isEmpty else { return nil } // Find the minimum and maximum latitude and longitude values of the search var minLat = coordinates[0].latitude var maxLat = coordinates[0].latitude var minLon = coordinates[0].longitude var maxLon = coordinates[0].longitude for coordinate in coordinates { minLat = min(minLat, coordinate.latitude) maxLat = max(maxLat, coordinate.latitude) minLon = min(minLon, coordinate.longitude) maxLon = max(maxLon, coordinate.longitude) } // Calculate the region based on the search values let center = CLLocationCoordinate2D(latitude: (minLat + maxLat) / 2, longitude: (minLon + maxLon) / 2) let latitudeDelta = maxLat - minLat let longitudeDelta = maxLon - minLon let paddingFactor = 1.2 let span = MKCoordinateSpan( latitudeDelta: latitudeDelta * paddingFactor, longitudeDelta: longitudeDelta * paddingFactor ) let region = MKCoordinateRegion(center: center, span: span) cameraPosition = .region(region) return region } func getDirections() { route = nil isShowingBottomSheet = true if let userLocation = locationManager.userLocation { let coordinate = userLocation let request = MKDirections.Request() request.source = MKMapItem(placemark: MKPlacemark(coordinate: coordinate)) request.destination = selectedResult Task { let directions = MKDirections(request: request) let response = try? await directions.calculate() route = response?.routes.first } } } func shareLocation() -> URL? { guard let sourceLatitude = locationManager.manager.location?.coordinate.latitude, let sourceLongitude = locationManager.manager.location?.coordinate.longitude, let destinationLatitude = selectedResult?.placemark.coordinate.latitude, let destinationLongitude = selectedResult?.placemark.coordinate.longitude else { return nil } let googleMapsInstalled = UIApplication.shared.canOpenURL(URL(string: "comgooglemaps://")!) if googleMapsInstalled { return URL(string: "comgooglemaps://?saddr=\(sourceLatitude),\(sourceLongitude)&daddr=\(destinationLatitude),\(destinationLongitude)&directionsmode=driving") } else { return URL(string: "https://maps.apple.com?saddr=\(sourceLatitude),\(sourceLongitude)&daddr=\(destinationLatitude),\(destinationLongitude)") } } }