I am trying to add an annotation to the map on click, and would also like to show the callout.
I have added a tap gesture recognizer:
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGesture))
gestureRecognizer.numberOfTapsRequired = 1
gestureRecognizer.cancelsTouchesInView = true
mapView.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.delegate = self
}
And on tap:
@objc func tapGesture(_ sender: UITapGestureRecognizer? = nil) {
if let removeAnnotation = annotation {
mapView.removeAnnotation(removeAnnotation)
}
guard let point = sender?.location(in: mapView) else { return }
let annotation = MKPointAnnotation()
annotation.coordinate = mapView.convert(point, toCoordinateFrom: mapView)
annotation.title = "Title"
annotation.subtitle = "Subtitle"
mapView.addAnnotation(annotation)
self.annotation = annotation
}
From there I have a map delegate to show the annotation:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "Annotation"
var annotationView: MKAnnotationView? = nil
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
dequeuedView.annotation = annotation
annotationView = dequeuedView
} else {
annotationView = MKAnnotationView(annotation: annotation,reuseIdentifier: identifier)
annotationView?.canShowCallout = true
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView?.image = UIImage(named: "icon")
}
return annotationView
}
And when the annotationview is added I select the annotation to show the callout:
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
if let annotation = views.first?.annotation {
mapView.selectAnnotation(annotation, animated: true)
}
}
However shortly after the annotation callout is shown it disappears
Digging in a little deeper, I think that there is a tap gesture recognizer on the mapview that is fired after my tap that adds the callout, which then deselects the annotation removing the callout.
Which lead me to add a delegate method to my tap gesture;
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if (otherGestureRecognizer is UITapGestureRecognizer) {
return true
}
return false
}
So at that point I am disabling all existing MapView taps gestures, not really what I want. Looking further at all the tap gestures that are on the map view, this one (_handleSelectGestureToDeselect) looks to be handling the deselect of annotations on map click. Which I imagine what is deselecting my annotation.
<UITapGestureRecognizer: 0x7f99e2418db0; state = Possible; delaysTouchesEnded = NO; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=_handleSelectGestureToDeselect:, target=<MKMapView 0x7f99e302ae00>)>; must-fail = {
<UITapGestureRecognizer: 0x7f99e2418a90; state = Possible; delaysTouchesEnded = NO; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=_handleSelectGestureToSelect:, target=<MKMapView 0x7f99e302ae00>)>>,
<UITapGestureRecognizer: 0x7f99e2417b50; state = Possible; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=handleTwoFingerTap:, target=<MKMapGestureController 0x7f99e2409940>)>; numberOfTouchesRequired = 2>,
<MKVariableDelayTapRecognizer: 0x7f99e24077e0; baseClass = UITapGestureRecognizer; state = Possible; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=handleDoubleTap:, target=<MKMapGestureController 0x7f99e2409940>)>; numberOfTapsRequired = 2>,
<_MKOneHandedZoomGestureRecognizer: 0x7f99e241e7b0; baseClass = UIGestureRecognizer; state = Possible; delaysTouchesEnded = NO; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=handlePanZoom:, target=<MKMapGestureController 0x7f99e2409940>)>>,
<UIPanGestureRecognizer: 0x7f99e240bb60; state = Possible; view = <_MKMapContentView 0x7f99b2404bc0>; target= <(action=handlePan:, target=<MKMapGestureController 0x7f99e2409940>)>>
}>
However all I can do is get a list of gesture recognizers for the map view, there is really no good way to pick out a specific tap gesture and disable it.
Last thing. This works but is a complete hack and makes the UI laggy. I can add a delay, seems it needs to be over 500 milliseconds to be clear of the mapview gesture unselecting the annotation:
let after = DispatchTime.now() + .milliseconds(500)
DispatchQueue.main.asyncAfter(deadline: after) {
mapView.selectAnnotation(annotation, animated: true)
}
So short of making the UI super slow, I don't really have any good options. Anyone run into this an come up with a work around? Is this a bug that I should file with Apple? Seems I should be able to add a gesture recognizer that cancels/overrides the other internal mapview gesture recognizers.
