Search code examples
swiftswiftuimkmapviewuitapgesturerecognizeruiviewrepresentable

Tapping an MKMapView in SwiftUI


I have a map in a SwiftUI app. It is working up to a point; but now I want to be able to tap on it and know the latitude and longitude of the tap. Here is the current code:

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    @Binding var centerCoordinate: CLLocationCoordinate2D
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        
        let gRecognizer = UITapGestureRecognizer(target: context.coordinator,
                                action: #selector(Coordinator.tapHandler(_:)))
        mapView.addGestureRecognizer(gRecognizer)
        return mapView
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        //print(#function)
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: MapView

        init(_ parent: MapView) {
            self.parent = parent
        }

        let gRecognizer = UITapGestureRecognizer(target: self,
                                                 action: #selector(tapHandler(_:)))
        
        @objc func tapHandler(_ gesture: UITapGestureRecognizer) {
            print(#function)
            .... get useful information here ...
        }
    }
}

In this state I can see when I tap, but I don't get the information I need (.i.e coordinates of the tap). I have tried a few variations of the code after searching the net. At this point it is not yet working. Any relevant tip on the way to go would be very welcome.


Solution

  • I had a similar situation, and this is what I did. I made Coordinator UIGestureRecognizerDelegate, and ensure gRecognizer delegate is set to it, and add it to the map. Something like:

    struct MapView: UIViewRepresentable {
    @Binding var centerCoordinate: CLLocationCoordinate2D
    
    let mapView = MKMapView()
    
    func makeUIView(context: Context) -> MKMapView {
        mapView.delegate = context.coordinator
        return mapView
    }
    
    func updateUIView(_ view: MKMapView, context: Context) {
        //print(#function)
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    
    class Coordinator: NSObject, MKMapViewDelegate, UIGestureRecognizerDelegate {
        var parent: MapView
    
        var gRecognizer = UITapGestureRecognizer()
    
        init(_ parent: MapView) {
            self.parent = parent
            super.init()
            self.gRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler)) 
            self.gRecognizer.delegate = self
            self.parent.mapView.addGestureRecognizer(gRecognizer)
        }
    
        @objc func tapHandler(_ gesture: UITapGestureRecognizer) {
            // position on the screen, CGPoint
            let location = gRecognizer.location(in: self.parent.mapView)
            // position on the map, CLLocationCoordinate2D
            let coordinate = self.parent.mapView.convert(location, toCoordinateFrom: self.parent.mapView)
            
        }
    }
    }