5

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.

Tigran Iskandaryan
  • 1,371
  • 2
  • 14
  • 41
  • 2
    If you aren't going to use the standard annotation view reuse identifier (which you can't in this case because you want different views) then you will need to implement the `MKMapViewDelegate` function `mapView(_:viewFor:)` to return the appropriate view – Paulw11 Oct 30 '17 at 00:33
  • @Paulw11 Thank you very much! – Tigran Iskandaryan Oct 30 '17 at 13:16

2 Answers2

5

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
    }
}
Kosuke Ogawa
  • 7,383
  • 3
  • 31
  • 52
2

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.

Paul King
  • 1,881
  • 20
  • 23