Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to register two or more custom annotation views?

I'm trying to register custom annotation views (I create them as a subclass of MKAnnotationView), but I have some trouble with that. Initially, I've tried to register only one custom annotation view for a single annotation marker using the code below (RestaurantMarkerView is my custom annotation view):

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

It works fine (when I tap the pin, it shows the callout). Now I add another annotation marker and try to register another view for it, and as far as I know, in that case I should use custom identifiers for the views, so I do that by this code:

  mapView.register(RestaurantMarkerView.self, forAnnotationViewWithReuseIdentifier: "a")
  mapView.register(ChoosenRestaurantMarkerView.self, forAnnotationViewWithReuseIdentifier: "b")

Now when I tap the markers, they don't show callouts. When I change the reuse identifier of one of the markers to MKMapViewDefaultAnnotationViewReuseIdentifier, that marker shows the callout. So, how can I register multiple custom annotation views? Thanks.

like image 569
Tigran Iskandaryan Avatar asked Oct 29 '17 23:10

Tigran Iskandaryan


2 Answers

As @Paulw says, you will need to implement the MKMapViewDelegate function mapView(_:viewFor:) to return the appropriate view.

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    if annotation is MKUserLocation {
        return nil
    }

    if /* condition */ {
        let reuseId = "a"
        var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
        if pinView == nil {
            pinView = RestaurantMarkerView(annotation: annotation, reuseIdentifier: reuseId)
            pinView?.canShowCallout = true
        } else {
            pinView?.annotation = annotation
        }

        return pinView
    } else {
        let reuseId = "b"
        var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
        if pinView == nil {
            pinView = ChoosenRestaurantMarkerView(annotation: annotation, reuseIdentifier: reuseId)
            pinView?.canShowCallout = true
        } else {
            pinView?.annotation = annotation
        }

        return pinView
    }
}
like image 144
Kosuke Ogawa Avatar answered Nov 14 '22 21:11

Kosuke Ogawa


The accepted answer was correct usage for pre-iOS11, before mapView.register(, forAnnotationViewWithReuseIdentifier:) became available, and it is still allowed for iOS11+, but the answer is incorrect for the question that is asked.

See Apple's map annotation clustering example app, downloadable from the docs: https://developer.apple.com/documentation/mapkit/mkannotationview/decluttering_a_map_with_mapkit_annotation_clustering

You should register all of your annotation exactly as you did in the first part of the question:

mapView.register(UnicycleAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(BicycleAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(TricycleAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)

It will automatically generate the actual reuseIdentifier based on the name of the class that you are registering.

like image 20
Paul King Avatar answered Nov 14 '22 21:11

Paul King