无法将点击手势识别器添加到 SwiftUI MKMapView UIViewRepresentable

Posted

技术标签:

【中文标题】无法将点击手势识别器添加到 SwiftUI MKMapView UIViewRepresentable【英文标题】:Can't add Tap Gesture Recognizer to SwiftUI MKMapView UIViewRepresentable 【发布时间】:2021-06-18 05:57:28 【问题描述】:

我有一个具有 SwiftUI 生命周期的 SwiftUI 应用程序,我正在尝试点击手势 在 MKMapView 中工作。我似乎无法正确获得对地图的引用。所有我 在本小节中想要做的是点击地图并添加注释。那时我会 也使用注解的坐标。

这是代码。

struct MapView: UIViewRepresentable 

    typealias UIViewType = MKMapView
    @State private var myMapView: MKMapView?

    class Coordinator: NSObject, MKMapViewDelegate 
        var control: MapView
        let parent = MapView()
    
        let sfCoord = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)

        init(_ control: MapView) 
            self.control = control
        
    
        func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) 
            if let annotationView = views.first 
                if let annotation = annotationView.annotation 
                    if annotation is MKUserLocation 
                        let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 2000, longitudinalMeters: 2000)
                        mapView.setRegion(region, animated: true)
                    
                
            
        //did add
    
        @objc func addAnnotationOnTapGesture(sender: UITapGestureRecognizer) 
            if sender.state == .ended 
                print("in addAnnotationOnTapGesture")
                let point = sender.location(in: parent.myMapView)
                print("point is \(point)")
                let coordinate = parent.myMapView?.convert(point, toCoordinateFrom: parent.myMapView)
                print("coordinate?.latitude is \(String(describing: coordinate?.latitude))")
                let annotation = MKPointAnnotation()
                annotation.coordinate = coordinate ?? sfCoord
                annotation.title = "Start"
                parent.myMapView?.addAnnotation(annotation)
            
        
    //coord

    func makeUIView(context: Context) -> MKMapView 
        let map = MKMapView()
        map.showsUserLocation = true
        map.delegate = context.coordinator
    
        DispatchQueue.main.async 
            self.myMapView = map
        
    
        let gRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.addAnnotationOnTapGesture(sender:)))
        map.addGestureRecognizer(gRecognizer)

        return map
    

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    func updateUIView(_ uiView: MKMapView, context: Context) 

    
//struct MapView

还有 LocationManager 供参考:

class LocationManager: NSObject, ObservableObject 
    private let locationManager = CLLocationManager()
    @Published var location: CLLocation? = nil

    override init() 
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.distanceFilter = kCLDistanceFilterNone
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    


extension LocationManager: CLLocationManagerDelegate 
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) 
        print(status)
    
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) 
        guard let location = locations.last else  return 
    
        self.location = location
    
//extension

最后是 ContentView:

struct ContentView: View 

    @ObservedObject var locationManager = LocationManager()

    var body: some View 
        MapView()
    

点击地图会显示如下控制台输出:

在addAnnotationOnTapGesture中

点是 (156.0, 515.6666564941406)

坐标?纬度为零

任何指导将不胜感激。 Xcode 12.5 ios 14.5

【问题讨论】:

【参考方案1】:

在您的Coordinator 中,您有两个对MapViews 的引用。一个 (control) 设置在 init 中,代表您想要的实际视图。另一个 (parent) 在您的 Coordinator 中定义,实际上并不是视图层次结构的一部分。因此,当您尝试从中获取坐标时,它会返回nil。您可以将所有对control 的引用更改为有效:

class Coordinator: NSObject, MKMapViewDelegate 
    var control: MapView

    let sfCoord = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)

    init(_ control: MapView) 
        self.control = control
    

    func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) 
        if let annotationView = views.first 
            if let annotation = annotationView.annotation 
                if annotation is MKUserLocation 
                    let region = MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 2000, longitudinalMeters: 2000)
                    mapView.setRegion(region, animated: true)
                
            
        
    //did add

    @objc func addAnnotationOnTapGesture(sender: UITapGestureRecognizer) 
        if sender.state == .ended 
            print("in addAnnotationOnTapGesture")
            let point = sender.location(in: control.myMapView)
            print("point is \(point)")
            let coordinate = control.myMapView?.convert(point, toCoordinateFrom: control.myMapView)
            print("coordinate?.latitude is \(String(describing: coordinate?.latitude))")
            let annotation = MKPointAnnotation()
            annotation.coordinate = coordinate ?? sfCoord
            annotation.title = "Start"
            control.myMapView?.addAnnotation(annotation)
        
    
//coord

【讨论】:

以上是关于无法将点击手势识别器添加到 SwiftUI MKMapView UIViewRepresentable的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 将点击手势添加到 UITextField

如果将点击手势识别器添加到其背景视图中,则不会调用 iOS didSelectItemAtIndexPath

添加手势后SwiftUI TextField不起作用

将手势识别器添加到表格单元格中的图像视图

将点击手势添加到任何视图都不会触发

为啥在 SwiftUI 中点击按钮的手势甚至在它的框架之外也会被触发?