SwiftUI / MapKit 注释选择和导航

SwiftUI / MapKit Annotation Selection and Navigation

我目前有一个 SwiftUI 视图,它显示了一张充满图钉的地图。地图有一个委托,可以确定何时选择图钉并获取与该图钉关联的自定义对象。

问题是,一旦委托人确定已选择 pin,我需要将我的 SwiftUI 视图 navigate/push 转到包含委托人检索的自定义对象的详细信息页面。

我能想到的最佳比较是尝试将所选注释视为主视图的导航 link 按钮。

如何从地图委托与 SwiftUI 视图进行通信?


import SwiftUI
import MapKit

/// The SwiftUI view I am referring to
struct ContentView: View {
    var body: some View {
        ZStack {
            MapView()
        }
        .edgesIgnoringSafeArea(.all)
    }
}


/// The custom object I am referring to
class LandmarkAnnotation: NSObject, MKAnnotation {
    let title: String?
    let subtitle: String?
    let coordinate: CLLocationCoordinate2D
    init(title: String?,
         subtitle: String?,
         coordinate: CLLocationCoordinate2D) {
        self.title = title
        self.subtitle = subtitle
        self.coordinate = coordinate
    }
}

/// The delegate I am referring to
class MapViewCoordinator: NSObject, MKMapViewDelegate {
    var mapViewController: MapView
    
    init(_ control: MapView) {
        self.mapViewController = control
    }
    
    func mapView(_ mapView: MKMapView, viewFor
                    annotation: MKAnnotation) -> MKAnnotationView?{
        ...
        return annotationView
    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let circle = MKCircleRenderer(overlay: overlay)
        ...
        return circle
    }
    
    /// This is where the delegate gets the object for the selected annotation
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        if let v = view.annotation as? LandmarkAnnotation {
            print(v.coordinate)
        }
    }
}


struct MapView: UIViewRepresentable {
    
    var markers: [CLLocationCoordinate2D] = [CLLocationCoordinate2D(
                                                latitude: 34.055404, longitude: -118.249278),CLLocationCoordinate2D(
                                                    latitude: 34.054097, longitude: -118.249664), CLLocationCoordinate2D(latitude: 34.053786, longitude: -118.247636)]
    var convertedMarkers: [LandmarkAnnotation] = []
    
    init() {
        convertedMarkers = cordToMark(locations: self.markers)
    }
    
    func makeUIView(context: Context) -> MKMapView{
        MKMapView(frame: .zero)
    }
    
    func cordToMark(locations: [CLLocationCoordinate2D]) -> [LandmarkAnnotation] {
        var marks: [LandmarkAnnotation] = []
        for cord in locations {
            let mark = LandmarkAnnotation(title: "Test", subtitle: "Sub", coordinate: cord)
            marks.append(mark)
        }
        return marks
    }
    
    func makeCoordinator() -> MapViewCoordinator{
        MapViewCoordinator(self)
    }
    
    func updateUIView(_ view: MKMapView, context: Context){
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.0537767, longitude: -118.248)
        let mapCamera = MKMapCamera()
        mapCamera.centerCoordinate = coordinate
        mapCamera.pitch = 10
        mapCamera.altitude = 3000
        view.camera = mapCamera
        view.mapType = .mutedStandard
        view.delegate = context.coordinator
        view.addAnnotations(self.convertedMarkers)
        let radiusCircle = MKCircle(center: CLLocationCoordinate2D(
                                        latitude: 34.0537767, longitude: -118.248), radius: 300 as CLLocationDistance)
        view.addOverlay(radiusCircle)
        let locationCircle = MKCircle(center: CLLocationCoordinate2D(
                                        latitude: 34.0537767, longitude: -118.248), radius: 3 as CLLocationDistance)
        view.addOverlay(locationCircle)
    }
    
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environment(\.colorScheme, .light)
    }
}


这是模拟器中显示的地图图像

这是一个想法(草稿)

  1. 向可表示对象添加回调
struct MapView: UIViewRepresentable {
    var didSelect: (LandmarkAnnotation) -> ()  // callback
  1. 所以在ContentView
ZStack {
    MapView() { annotation in
       // store/pass annotation somewhere, and
       // activate navigation link here, eg. via isActive or selection
    }
}
  1. 在委托中激活回调
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    if let v = view.annotation as? LandmarkAnnotation {
        print(v.coordinate)

       self.mapViewController.didSelect(v)    // << here !!
    }
}