Search code examples
swiftuimkmapviewmkmapviewdelegate

How to make custom MKMapView delegate actions for SwiftUI ViewRepresentable?


I've wrapped a MKMapView in a ViewRepresentable

public struct MapView: UIViewRepresentable {

I want to create an action callback that works like this:

MapView().onAnnotationTapped { annotation in
   
}

In MapView i have defined

@inlinable public func onAnnotationTapped(site: (AnnotationView) -> ()) -> some View {
    return self
}

But how to provide the AnnotationView from the coordinator?

public class MapViewCoordinator: NSObject, MKMapViewDelegate {
    var mapView: MapView
    
    init(_ control: MapView) {
        self.mapView = control
    }
    
    public func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {

  /// How to send from here to the MapView function? 

    }
} 
           

Solution

  • Here is a solution - introduce callback property for view and inject it in your inalinable modifier. Prepared with Xcode 12.1 / iOS 14.1

    struct MapView: UIViewRepresentable {
        func makeUIView(context: Context) -> MKMapView {
            let view = MKMapView()
            view.delegate = context.coordinator
            return view
        }
        
        func updateUIView(_ uiView: MKMapView, context: Context) {
        }
    
        func makeCoordinator() -> MapViewCoordinator {
            MapViewCoordinator(self)
        }
        
        var tapCallback: ((MKAnnotationView) -> ())?    // << this one !!
        
        @inlinable public func onAnnotationTapped(site: @escaping (MKAnnotationView) -> ()) -> some View {
            var newMapView = self
            newMapView.tapCallback = site            // << here !!
            return newMapView
        }
        
        public class MapViewCoordinator: NSObject, MKMapViewDelegate {
            var mapView: MapView
            
            init(_ control: MapView) {
                self.mapView = control
            }
            
            public func mapView(_ mkMap: MKMapView, didSelect view: MKAnnotationView) {
                self.mapView.tapCallback?(view)     // << call !!
            }
        }
    }