Skip to content

janodev/callout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Annotations with custom callouts

Based on code by Asynchrony Solutions and several authors.
See http://stackoverflow.com/questions/8018841/customize-the-mkannotationview-callout/8019308#8019308

XIB based annotation

Strategy

We can't create a XIB backed callout, but we can customize the annotation 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 results in a completely customizable virtual callout.

Class diagram

What we see in this image is:

  • AnnotationProtocol: protocol to implement by annotations who wish to handle view creation themselves.
  • AnnotationViewProtocol: protocol to implement by annotations who wish to handle view selection themselves.
  • Annotation & AnnotationView: the real annotation.
  • CalloutAnnotation & CalloutAnnotationView: the fake callout bubble.
  • BaseCalloutView: MKAnnotationView.
  • Content: This class is passed along with the final data to create the callout.

To add an annotation to the map:

CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(0.,0.);
Annotation *ann = [[Annotation alloc]initWithCoordinate:coord]
[self.mapView addAnnotation:ann];

After adding an annotation, the MKMapViewDelegate is invoked, which in turn delegates the view creation to the annotation itself.

-[MKMapViewDelegate mapView:viewForAnnotation:]
  -[Annotation annotationViewInMap] // view creation is delegated to the annotation
    -[AnnotationView initWithAnnotation:reuseIdentifier:] // the view is initialized with the annotation

Now we have a map with a Annotation and AnnotationView on it. Touching the annotation view calls MKMapViewDelegate, which in turn delegates the selection to the view. The view creates a CalloutAnnotation and adds it to the map.

-[MKMapViewDelegate mapView:didSelectAnnotationView:]
  -[AnnotationView didSelectAnnotationViewInMap:]
    -[CalloutAnnotation initWithLat:lon:]

Now we have the same thing, the view creation is delegated to CalloutAnnotation, which creates a CalloutAnnotationView, which uses Core Graphics to create a callout bubble.

CalloutView has a CalloutView.XIB. The code to paint the bubble around the XIB is in the superclass BaseCalloutView.

Classes

AnnotationProtocol
  annotationViewInMap:

AnnotationViewProtocol
  didSelectAnnotationViewInMap:
  didDeselectAnnotationViewInMap:

Annotation        : NSObject         <MKAnnotation, AnnotationProtocol>
AnnotationView    : MKAnnotationView <AnnotationViewProtocol>
BaseCalloutView   : MKAnnotationView <AnnotationViewProtocol>
CalloutAnnotation : NSObject         <MKAnnotation, AnnotationProtocol>
CalloutView       : BaseCalloutView 

Behavior

Sequence diagram

Annotation        --(added)creates-->    AnnotationView      (the pin)
AnnotationView    --(selected)creates--> CalloutAnnotation   (the annotation acting as delegate to create the bubble)
CalloutAnnotation --(added)creates-->    CalloutView         (the bubble)
CalloutView       --paints-->            <fake callout>      (the bubble painting code)

Usage

To create an annotation:

Content *content = [Content new];
content.iconURL = [[NSBundle mainBundle] URLForResource:@"emoji-ghost" withExtension:@"png"];
content.calloutView = [MyCalloutView class];
content.coordinate = coord;
content.values = [NSDictionary dictionaryWithObjectsAndKeys:@"Booo!",@"title", nil];
Annotation *anno = [[Annotation alloc] initWithContent:content];

Then on MyCalloutView:

#import "CalloutView.h"
#import "CalloutAnnotation.h"
@interface MyCalloutView : CalloutView
@property (nonatomic, retain) IBOutlet UILabel* title;
-(IBAction) handleTouch:(id)sender;
- (id) initWithAnnotation:(CalloutAnnotation*)annotation;
@end

#import "MyCalloutView.h"
@implementation MyCalloutView
@synthesize title = _title;
-(IBAction) handleTouch:(id)sender {
    debug(@"touch %@", sender);
}
- (id)initWithAnnotation:(CalloutAnnotation*)annotation {
    self = [super initWithAnnotation:annotation];
    self.title.text = [annotation.content.values objectForKey:@"title"];
    return self;
}
- (void)dealloc {
    self.title = nil;
    [super dealloc];
}
@end

About

MKAnnotation callout with a custom XIB

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published