0
votes

I am displaying a set of annotations on an MKMapView, using a custom MKMarkerAnnotationView with displayPriority = .required so that there is no clustering or hiding, and a UIButton as its rightCalloutAccessoryView.

When I tap an annotation on the map, the callout displays as expected, but when I tap the callout or its accessory, if the tap overlaps with another marker on the map, the tap doesn't register.

Below is a Playground-friendly example of the problem. Notice how the callout doesn't respond to taps when it overlaps with another annotation on the map.

import MapKit
import PlaygroundSupport

class MapViewController: UIViewController, MKMapViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
                 calloutAccessoryControlTapped control: UIControl) {
        print("Callout tapped!")
    }
}

class CustomAnnotationView: MKMarkerAnnotationView {
    override var annotation: MKAnnotation? {
        willSet {
            canShowCallout = true
            rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
            titleVisibility = .hidden
            subtitleVisibility = .hidden
            displayPriority = .required
        }
    }
}

let mapView = MKMapView(frame: CGRect(x:0, y:0, width:800, height:800))
let controller = MapViewController()
mapView.delegate = controller

mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)

let coordinate1 = CLLocationCoordinate2DMake(37.334922, -122.009033)
let annotation1 = MKPointAnnotation()
annotation1.coordinate = coordinate1
annotation1.title = "Annotation 1"
annotation1.subtitle = "Subtitle 1"

let coordinate2 = CLLocationCoordinate2DMake(37.335821492347556, -122.0071341097355)
let annotation2 = MKPointAnnotation()
annotation2.coordinate = coordinate2
annotation2.title = "Annotation 2"
annotation2.subtitle = "Subtitle 2"

mapView.addAnnotation(annotation1)
mapView.addAnnotation(annotation2)

var mapRegion = MKCoordinateRegion()
let mapRegionSpan = 0.02
mapRegion.center = coordinate1
mapRegion.span.latitudeDelta = mapRegionSpan
mapRegion.span.longitudeDelta = mapRegionSpan
mapView.setRegion(mapRegion, animated: true)

let mapViewController = MapViewController()
PlaygroundPage.current.liveView = mapView

And an image to illustrate the problem.

Overlapping annotations

Any help on this would be very appreciated. Thanks!

1

1 Answers

0
votes

My teammate was able to solve this issue. The idea is to disable user interaction for all other annotations on the map when an annotation is selected, and then re-enable it when the annotation is deselected.

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    for nearbyAnnotation in mapView.annotations {
        let annotationView = mapView.view(for: nearbyAnnotation)
        if annotationView != nil {
            annotationView!.isUserInteractionEnabled = false
        }
    }
    view.isUserInteractionEnabled = true
}

func mapView(_ mapView: MKMapView, didDeselect _: MKAnnotationView) {
    for nearbyAnnotation in mapView.annotations {
        let annotationView = mapView.view(for: nearbyAnnotation)
        if annotationView != nil {
            annotationView!.isUserInteractionEnabled = true
        }
    }
}