Customize the MKAnnotationView callout Customize the MKAnnotationView callout ios ios

Customize the MKAnnotationView callout


I understand you want a pin with a custom callout.

We can't create a custom callout, but we can create an annotation with a completely customized view. So the trick is to add a second annotation when the first is selected, and make the 2nd annotation view look like a callout bubble.

This is the solution posted by users djibouti33 and jacob-jennings in the answer: MKAnnotationView - Lock custom annotation view to pin on location updates, which in turn is based in a blog post from Asynchrony Solutions. For explanation purposes, here is some UML from a forked project:Annotation with custom XIB

This is a big hack, but also the cleanest way I've seen to implement custom annotations.

Start with a NSObject "Content" class which has a coordinate, the class of the callout view to use (in the UML is AnnotationView, but you can create more and set them here), and a dictionary of random values with the title, photo url, etc. Use this class to initialize a MKAnnotation "Annotation" object.

#import <MapKit/MapKit.h>@interface Content : NSObject@property (nonatomic,assign) CLLocationCoordinate2D coordinate;// ...@interface Annotation : NSObject <MKAnnotation, AnnotationProtocol>-(id) initWithContent:(Content*)content;// ...

The Annotation implements AnnotationProtocol to announce it wants to handle the creation of its own MKAnnotationView. That is, your MKMapViewDelegate should have code like this:

- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation {    // if this is a custom annotation, delegate the implementation of the view    if ([annotation conformsToProtocol:@protocol(AnnotationProtocol)]) {        return [((NSObject<AnnotationProtocol>*)annotation) annotationViewInMap:mapView];    } else {        // else, return a standard annotation view        // ...    }} 

The view returned will be of type AnnotationView, which implements AnnotationViewProtocol to announce that it wants to handle selection/deselection. Therefore, in your map view controller, the methods mapView:didSelectAnnotationView: and mapView:didDeselectAnnotationView: should delegate in a similar way to what we saw before.

When the annotation is selected, a second annotation (CalloutAnnotation) is added, which follows the same behaviour, but this time the view returned (CalloutView) is initialized from a XIB, and contains Core Graphics code (in BaseCalloutView) to animate and replicate a callout.

The initializer of the CalloutView class:

- (id)initWithAnnotation:(CalloutAnnotation*)annotation{    NSString *identifier = NSStringFromClass([self class]);    self = [super initWithAnnotation:annotation reuseIdentifier:identifier];    if (self!=nil){        [[NSBundle mainBundle] loadNibNamed:identifier owner:self options:nil];        // prevent the tap and double tap from reaching views underneath        UITapGestureRecognizer *tapGestureRecognizer = ...    }    return self;}

To be able to push another view controller from the callout view I used notifications.

The SO answer I linked at the top contains two complete projects implementing this code (class names may differ). I have another project using the UML above at https://github.com/j4n0/callout.


I added custom UIButton in MKAnnotationView. And on click of that button I have shown popOver with rootViewController with the view similar as you have shown above.


I know this question is from 2011 but for people who still find it in a search:In iOS 9 you have MKAnnotationView.detailCalloutAccessoryView which entirely replaces the standard callout.