Tapping an Mkmapview in Swiftui

Tapping an MKMapView in SwiftUI

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)

}
}
}

Can't add Tap Gesture Recognizer to SwiftUI MKMapView UIViewRepresentable

In your Coordinator, you have two references to MapViews. One (control) is set in init and represents the actual view that you want. The other (parent) is defined within your Coordinator and is not actually part of the view hierarchy. Therefore, when you try to get a coordinate from it, it returns nil. You can change all of the references to control and it works:

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 Button on top of a MKMapView does not get triggered

Give it just a bit more internal space to be better recognizable. Here is fixed & tested variant (Xcode 12 / iOS 14):

struct TestButtonWithMap: View {
@State private var locked = true
var body: some View {
ZStack {
MapView()
Button(action: {
print("Tapped")
self.locked.toggle()
}) {
Image(systemName: locked ? "lock.fill" : "lock.open")
.padding() // << here !!
}
}
}
}

How to call a function or tell if a MKPointAnnotation is clicked on a MKMapView SwiftUI

Assuming that you're wrapping your MKMapView inside a UIViewRepresentable struct, add a coordinator with the MKMapViewDelegate protocol to listen for changes on your map:

//Inside your UIViewRepresentable struct
func makeCoordinator() -> Coordinator {
Coordinator()
}

class Coordinator: NSObject, MKMapViewDelegate {
//Delegate function to listen for annotation selection on your map
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation {
//Process your annotation here
}
}
}

There are a couple of tutorials out there on how to include an MKMapView in SwiftUI and use delegation to access the MKMapViewDelegate functions through UIViewRepresentable and coordinators.

Following along my suggestion, your previous code would look like so:

struct MapKitView: UIViewRepresentable {

typealias Context = UIViewRepresentableContext<MapKitView>

func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.delegate = context.coordinator
let annotation = MKPointAnnotation()

annotation.coordinate = donator.coordinates
annotation.title = donator.name
annotation.subtitle = donator.car
map.addAnnotation(annotation)
return map
}

//Coordinator code
func makeCoordinator() -> Coordinator { ... }
}


Related Topics



Leave a reply



Submit