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 MapView
s. 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
Parse.Com Pfgeopoint.Geopointforcurrentlocationinbackground Not Doing Anything
Disable Https Get Certificate Check in Swift 5
Possible to Write Swift Println Logs into File Too
Hide View Item of Nsstackview with Animation
Swift - Writing a Byte Stream to File
The Right Place to Call .Removeobserver for Nsnotificationcenter = Swift Deinit()
iOS Swift - Uitableviewcell Custom Subclass Not Displaying Content
Why Specify [Unowned Self] in Blocks Where You Depend on Self Being There
Trouble with Non-Escaping Closures in Swift 3
How to Properly Check If Non-Optional Return Value Is Valid
Usage of Withmemoryrebound with Apples Swift 3 Beta 6
Accessing Global Variable in Swift
Passing a Variable Through a Segue? Xcode 8 Swift 3
Swift Spritekit Arc for Dummies