Skip to content

Using MKAnnotation, MKPinAnnotationView and creating a custom MKAnnotationView in an MKMapView

My last experiment maps on the iPhone using the MKMapView from the iPhone’s MapKit was an example of how to use the MKMapView to display the line of a route on the map.

Today’s experiment will demonstrate how to drop pin annotation views, as well as custom annotation views on the iphone’s map by providing the map with objects that implement the MKAnnotation protocol.

The class I created that implements MKAnnotation can be used to differentiate between those annotations that should be represented by MKPinAnnotationViews of diferernt colors, as well as our custom annotation view, CSImageAnnotationView. The CSMapAnnotation class needs to keep track of additional information such as images and URLs that can be used for a custom display.

This is the header for the custom annotation. You can see that there is an enumeration that is used to designate what type of annotation view should be used for display of each annotation on the iPhone’s map. Also available are the userData and url properties; userData can be used to store anything, but in the case of this example, we’re using it to store path information for an image that should be displayed on the map. Title is also an available property, but subtitle will be generated on the fly.

// CSMapAnnotation.h
// mapLines
// Created by Craig on 5/15/09.
// Copyright 2009 Craig Spitzkoff. All rights reserved.


// types of annotations for which we will provide annotation views.
typedef enum {
CSMapAnnotationTypeStart = 0,
CSMapAnnotationTypeEnd = 1,
CSMapAnnotationTypeImage = 2
} CSMapAnnotationType;

@interface CSMapAnnotation : NSObject
CLLocationCoordinate2D _coordinate;
CSMapAnnotationType _annotationType;
NSString* _title;
NSString* _subtitle;
NSString* _userData;
NSURL* _url;

-(id) initWithCoordinate:(CLLocationCoordinate2D)coordinate
annotationType:(CSMapAnnotationType) annotationType

@property CSMapAnnotationType annotationType;
@property (nonatomic, retain) NSString* userData;
@property (nonatomic, retain) NSURL* url;


In most cases, when our MKMapViewDelegate is asked for an MKAnnotation, we return a MKPinAnnotation. If the CSMapAnnotation is set to be CSMapAnnotationTypeImage , the delegate instead produces an instance of our custom annotation view, CSImageAnnotationView, and sets its properties based on the annotation’s user data.

This is the body of our custom annotation view. It is pretty simple; the bulk of the functionality is in the initialization function, which sets up the view to have a UIImageView.

// CSImageAnnotationView.m
// mapLines
// Created by Craig on 5/15/09.
// Copyright 2009 Craig Spitzkoff. All rights reserved.

#import "CSImageAnnotationView.h"
#import "CSMapAnnotation.h"

#define kHeight 100
#define kWidth 100
#define kBorder 2

@implementation CSImageAnnotationView
@synthesize imageView = _imageView;

- (id)initWithAnnotation:(id )annotation reuseIdentifier:(NSString *)reuseIdentifier
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
self.frame = CGRectMake(0, 0, kWidth, kHeight);
self.backgroundColor = [UIColor whiteColor];

CSMapAnnotation* csAnnotation = (CSMapAnnotation*)annotation;

UIImage* image = [UIImage imageNamed:csAnnotation.userData];
_imageView = [[UIImageView alloc] initWithImage:image];

_imageView.frame = CGRectMake(kBorder, kBorder, kWidth - 2 * kBorder, kWidth - 2 * kBorder);
[self addSubview:_imageView];

return self;


-(void) dealloc
[_imageView release];
[super dealloc];


So, how does the map know to ask the delegate for annotation views? It knows to do this based on annotations that are added to the map in our view controller’s viewDidLoad method.

// create the image annotation
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:[[points objectAtIndex:points.count / 2] coordinate]
title:@"Cleveland Circle"] autorelease];
[annotation setUserData:@"cc.jpg"];
[annotation setUrl:[NSURL URLWithString:@""]];

[_mapView addAnnotation:annotation];

UPDATE – 6/1/2009
You’ll notice we set the URL on the annotation above, and the code has now been updated to display that webpage when the user clicks the annotation disclosure of our custom image annotation view. The code sample you can download has been updated to reflect this change.

The full code of this example is available for download here.

Update – 8/19/2009
Another update. This one displays the route as an annotation view, so it does not render on top of other annotations.You can view the update at this link.

{ 56 } Comments

  1. jf | May 21, 2009 at 5:37 pm | Permalink

    Great stuff. Really gets me started on Mapkit.

    I was wondering: Can we use bits of this code without restriction?

  2. admin | May 21, 2009 at 7:06 pm | Permalink

    Yup.. use it however you want. I’m glad you found it useful.

  3. Bernard | May 23, 2009 at 1:34 pm | Permalink

    If your CSImageAnnotationView is also a descendant of the UIControl class, you can use the map view’s delegate to receive notifications when your control is tapped.

  4. jf | May 24, 2009 at 10:32 am | Permalink

    Thanks much.

    Another question: I noticed when I zoom in, the lines don’t seem to stay at the same scale as the map (lines get off the pixel points slightly). Do you see that, or is it something with my build perhaps?

  5. jf | May 31, 2009 at 10:06 am | Permalink

    Did you ever get the calloutaccessory button to trigger?

  6. ed | May 31, 2009 at 12:23 pm | Permalink

    Yes, I have also had no luck with the callout responding. Hopefully someone has figured it out. Still working on it! I am thinking there is some invisible layer? Sitting on top of it. Not sure, but have tried a zillion ideas so far.

  7. admin | May 31, 2009 at 1:34 pm | Permalink

    I just figured out how to get the calloutAccessoryControlTapped to trigger, wit thanks to ed’s comment about the “invisible layer”. Thats exactly waht the problem was, that our route layer was intercepting the click. The addition of one line of code in the viewDidLoad function on the CSMapRouteLayerView prevents the view from intercepting UI events. That line is:

    [self setUserInteractionEnabled:NO];

    I’ll upload an updated example in a few minutes that has the fix.

    Thanks for your help!

  8. ed | May 31, 2009 at 2:43 pm | Permalink

    Wow, cool fix. thanks!

    How did you master this stuff? It’s a lot of work I think. Is there ONE book you would take to a desert island explaining the ins and outs of ObjC/Cocoa? I’ll go out and get it.

    I’m playing with dropping zillions of pins on a map at once, it’s very crazy! I call it love bombing (want to replace the pins with hearts and peace symbols).

    Friends think I’m crazy, but just love the Mapkit.

    thanks! :-) ed

  9. jf | May 31, 2009 at 3:11 pm | Permalink

    Too cool. I was trying to resignfirstresponder, forward touches, etc. since I figured it had to be the map overlay, but of course it was so much simpler than that! (I really have to learn to code more elegantly!)

    Thanks for your support to the community.

  10. admin | May 31, 2009 at 3:21 pm | Permalink

    No problem… glad I could help. I’ve updated the attached code sample to actually do something now that the calloutAccessoryControlTapped gets triggered.

  11. Scott | June 2, 2009 at 8:37 pm | Permalink

    Thanks, I’m just getting started w/MKMapKit… and this is the first code I’ve seen that actually works. Well, until I break it, of course. :)

  12. Zaplitny | June 3, 2009 at 1:54 am | Permalink

    Has anybody got success in customizing calloutView. The problem is that height of calloutView is fixed and adding view with height greater than callout view’s makes UI ugly

  13. Lee Armstrong | June 6, 2009 at 6:37 am | Permalink

    No matter how I try and insert this into my project I cannot get the lines to appear!!!! Grr!

    Any ideas?

    All I want to do in essence is draw a single straight line at co-ords 0,0 as a test and can’t even do that.

    Can you help please for a numpty like me?

  14. admin | June 6, 2009 at 7:18 am | Permalink

    Lee… send me some code; I’ll be happy to take a look at it.

  15. Lee Armstrong | June 6, 2009 at 7:34 am | Permalink


    I actually just had a good crack at it and now have your points loaded onto my mapView! Great!

    All of the pins that I had (custom images) are now all red pushpins…also the callouts have gone :-(

    I’d rather not send some code over but any pointers welcome…

  16. Sanjay | June 11, 2009 at 2:49 am | Permalink

    Really many Many Thank you.
    May god bless you

  17. ck | June 19, 2009 at 4:45 am | Permalink

    Thanks! Is there any way to make the line appear below the annotation views?

  18. Bob | June 22, 2009 at 1:13 pm | Permalink

    Looks like HTML ate part of the #import lines ‘cuz of the angle brackets. Might wanna replace those with < and >

  19. Slava | June 23, 2009 at 10:27 am | Permalink

    Thanks for the saples.
    Please help, how to animate and move such imaged markers?

  20. Puppet | June 24, 2009 at 4:18 pm | Permalink

    First of all thank you very much for sharing this code!!!

    I too would like to know how to get the line to be below the annotations also. I’ve tried creating a 3rd layer like the CSMapRouteLayerView with an empty drawRect and adding annotations to it, but much to my dismay, no joy…

  21. Sebastian | June 24, 2009 at 4:50 pm | Permalink

    Thank you a lot for your great examples.
    It answered all my questions and i am happily adding lots
    of great maps to my application right now!

  22. jissa | June 26, 2009 at 4:31 am | Permalink

    hi, thanks for this tutorial ,but i have question:
    is there any code to make the pin(MKPinAnnotationView) move when the user moved or touch?

  23. Ross Kimes | June 27, 2009 at 12:01 am | Permalink

    What is the easiest way to display and image that will resize itself over the map instead of the line. My plan was to use your example, but to use an UIImageView instead of an UIView, but an UIImageView does not call -drawRect.

    Any ideas on how to get this done? Thanks!

  24. Ross Kimes | June 27, 2009 at 2:59 pm | Permalink

    Never mind I figured out image problem, but I would like to echo the question of how to get the lines below the annotations (in this case a pin annotation).


  25. Ed Scott | July 6, 2009 at 5:32 pm | Permalink

    Craig this is a GREAT little app to teach some things and helped me out more than any other to learn….Thanks!

    I do have a question, is there a way we can get the blue line to appear under the annotation? Often it crosses over my annotation making it hard to read.

  26. Seb | July 7, 2009 at 4:03 am | Permalink

    Hi, thank you for this great tutorial. It works perfectly into my project, but I have one problem :
    I have a NSTimer which refresh the imageAnnotation pin. The first time it works, but for the second, the images becomes red pins.

    What’s wrong ? Thank you for your help.

  27. Shawn | July 10, 2009 at 3:35 pm | Permalink

    Thanks for the great demo, I have a question and a problem though:
    You example uses and table of points for the lines, How would you determine the points between any two Lat/Lon ? (IE display a route line between Current position and another annotation on the map)

    Secondly, I can not seem to get the rightCalloutAccessoryView to work
    [pin setCanShowCallout:YES];
    pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

    Callout shows up, but the delegate function never gets called when button hit
    – (void)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation calloutAccessoryControlTapped:(UIControl *)control
    {NSLog(“@button pushed”);

  28. wanna | July 16, 2009 at 9:16 pm | Permalink

    Thank you for the blog.
    I got questions regarding to removeAnnotation and dequeueReusableAnnotationViewWithIdentifier.

    I have the following code to remove all the pins from the map.
    NSArray *annotationArrs = self.mapView.annotations;
    NSLog(@”Remove all annotations!”);
    [self.mapView removeAnnotations:annotationArrs];

    1/how do I check if there is no pin on the map then add all the pins back to the map?

    2/how do I remove and later deque a specific group of annotation back to the map?

    Thank you for your help.

  29. jissa | July 20, 2009 at 6:04 am | Permalink

    Thanks for the samples.
    is there any code to make the pin(MKPinAnnotationView) move when the user drag or touch?

  30. jassous | July 23, 2009 at 1:18 am | Permalink

    thanks for this example.
    my application will determine the distance between 2 pin that the user will putted. i put a pin on the map but i can move it ,i try pin.userintersectionenabled=yes + -(void)toucheBegan: but it’s not working can you help me?please?

  31. Ed Wrenbeck | August 14, 2009 at 4:22 pm | Permalink

    Thanks for the post Craig, like others it really helped me get started with the map view stuff.

    I was wondering why you didn’t implement the route as an annotation view, rather than as a separate view sitting on top of the map?

    I was running into the problem where the map line was being drawn over my pin and my pop up, which it looks like your sample code has the same.

    I reimplemented the route view to be an annotation view and now it sits nicely behind the pins and pop ups.

    The other benefit of doing it this way is that when the user is scrolling the map, the route can stay on screen and go with it. So now my only problem is when the map is being scaled the lines gets misplaced until the scaling is done. Now I’m still debating whether to hide or show the line when moving/scaling. Ideally of course, I’d like to figure out how to fix the scaling too.

    I haven’t decided if its just a bug in my code or not, which it probably is, but it seems like it stops drawing the annotation view with the route about 25-30 pixels from the top of the screen.

    I want to post my code, but its just too dirty right now, but hopefully I’ll get it whipped into shape.

    Thanks again, Ed

  32. Lars | August 17, 2009 at 7:12 pm | Permalink

    Hey I have a quick question. Im using your code and i modified a bit to draw a line of where the person is. the problem im having is that i get this error:
    Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Invalid Region ‘

    Why is it doing this? is it because of zoom? because that point does exist?
    any ideas?


  33. admin | August 20, 2009 at 8:36 am | Permalink

    Thanks for the comment Ed. I liked the idea you proposed of drawing the lines in an annotation view instead of as an overlaid view on the map, so I updated the code to do just that. Its somewhat more complicated due to the way a map view will move the annotation view offscreen and the size of the route actually being larger than the view itself, but I think I’ve gotten around that using a non-clipped subview of the annotation view that always stays at 0,0 relative to the map view.

    This will address other people’s concerns about drawing the line under other annotations instead of drawing on top of them.

    The updated code is here:

  34. Chris | September 6, 2009 at 10:38 am | Permalink


    great tutorial!, i have a simiar problem as seb, i’ve setup a timer to change the image and position of a annotationview, but can’t get it working properly,
    the stuff gets set but never results in a screen refresh.

    used your cvs file to create a bunch of markers and then:

    – (void) updateView:(NSTimer*)theTimer {

    for (CSMapAnnotation* csAnnotation in [_mapView annotations] ) {
    CSImageAnnotationView* imageAnnotationView = (CSImageAnnotationView*) [self mapView:_mapView viewForAnnotation:csAnnotation];
    if (blue) {
    NSLog(@”switching to blue”);
    [imageAnnotationView updateImage:@”blue_0.png”];
    else {
    NSLog(@”switching to red”);
    [imageAnnotationView updateImage:@”red.png”];
    if (blue) {
    blue = NO;
    else {
    blue = YES;
    [_mapView setNeedsDisplay];

    where update image is:

    – (void) updateImage:(NSString*) name {
    [_imageView setImage:[UIImage imageNamed:name]];
    [_imageView setNeedsDisplay];

    any idea?



  35. admin | September 7, 2009 at 3:20 am | Permalink

    Hi Chris,

    I’m not sure (I haven’t tested your specific code snippet) but performing the updateImage method on the main thread may help. So instead of calling [imageAnnotationView updateImage:…] you could try [imageAnnotationView performSelectorOnMainThread:@selector(updateImate:) withObject:…];

    Please let me know if that helps.


  36. River Tiger | September 9, 2009 at 5:37 am | Permalink

    Craig, I was curious as to whether its possible to extend CSMapAnnotation to have a bigger popup view…not just only to include a title, subtitle, URL, accessory chevron, but also a bigger content window…basically a larger view with more content….does MKAnnotation smart enough to scale that view window to be bigger?

  37. Jeremy | September 15, 2009 at 6:43 pm | Permalink

    Hi Craig,

    This is such a nice example, in fact best I could find, it makes it easier to understand than reading Apple’s documents.

    Just a quick question.. I am trying to draw a little car icon, do you know if it is possible to rotate the image such as your CC.jpg from 0-360 degrees? Also does the image support transparency because I want to show it as an icon and not an fully opaque picture.

    Thanks in advance

  38. kish | September 23, 2009 at 12:32 am | Permalink

    Hi Craig,

    I was also curious as to whether its possible to extend CSMapAnnotation to have a bigger popup(callout) view…not just only to include a title, subtitle, accessoryviews, but also a bigger content callout…basically a larger view with more content….does MKAnnotation smart enough to scale that view callout to be bigger? becoz default callout has constant height …

    thsnks in advance

  39. Lomiunefeme | October 29, 2009 at 4:14 pm | Permalink

    polycosanol tea Order Levlen effect of particle in insulating mineral oil mental health relating to alcahol arizona asthma and allergy scottsdale

  40. tnathos | November 9, 2009 at 12:49 pm | Permalink

    hi craig.

    thanks for share the code.. i have a probke y using title and subtilte but i cant autoajust the bubble so if i have a long string, the bubble show me aaaaaa…..

    how i can modify this? or how i can make a custom bubble?

  41. Stephen | December 31, 2009 at 10:57 am | Permalink

    Excellent tutorial and did quite a lot of what I wanted it to do out of the box.

    I too have a couple of questions! I implemented the route in an annotationView but my some of my point annotations are under and some lie over the route line. Any idea how this might happen? I need to overlay annotations on top of others and it would be great to be able to control the order.

    Looking at a previous post from Ed I too have a problem displaying the route at the top of the screen. The first time it is drawn it clips the top about 25 pixels from the screen edge. After scrolling however it draws fine even up to the edge.

    Many thanks

  42. Stephen | December 31, 2009 at 11:07 am | Permalink

    Apologies! I just read the solution on the other blog entry and all is solved on the overlaid annotations!

    The clipping of the route at the top of the screen is still an issue though if anyone has any pointers!


  43. Gordon McDowell | January 25, 2010 at 12:57 pm | Permalink

    Like Stephen, I’m also seeing clipping as I zoom in and out of lines. I made suggested change so lines refresh as zoom in & out, which makes it less visible… sort of flickers so user can keep moving until line visible… but if there’s a fix specific to that here I’m overlooking it.

    Also… Craig, it is not obvious to me why you track routes with a data dictionary instead of using the dequeueReusableAnnotationViewWithIdentifier like the images and pins? I mean in MapLinesViewController … – (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation

  44. admin | January 25, 2010 at 1:12 pm | Permalink


    Please see the updated entry at:

  45. nirkolounriodra | March 4, 2010 at 10:11 am | Permalink

    ok, good post.

  46. Grant | May 5, 2010 at 2:08 pm | Permalink

    Hey, this is great information! Thanks!

    I took what I learned from your approach and that of others and put it all together into a Github project that provides a set of classes to use to make this an easy thing to add to MKMapViews. Would love your feedback. It currently works with OS 3.2

  47. | June 19, 2010 at 11:05 am | Permalink

    Thanks for informative post. I am pleased sure this post has helped me save many hours of browsing other similar posts just to find what I was looking for. I bookmarked this blog a while ago because of the useful content and I am never being disappointed. Keep up the good work Just I want to say: Thank you!

  48. Merlin | July 3, 2010 at 1:18 pm | Permalink

    You saved me so much work – thank you for this and ?p=108 post. iOs 4 now has proper features to handle routes, but I guess that it will take some time before most people upgrade to iOs4..

  49. Bones | August 27, 2010 at 12:21 pm | Permalink

    Great Tutorial, I learned a lot. I did find an issue with the custom pin. The pin drops on a location, but the pin needle is not the spot that gets converted to Long/Lat. If the user drops the pin, the long lat is off by the size of the Pin.png. The Pin head is what converted to long lat.

    Also, I was curious as to why you choose to use Notification when you could have past a mapView reference to update the pin location?

    Like I said, great tutorial – just some questions and observations.


  50. drVacansS | October 22, 2010 at 1:00 am | Permalink

    В наше время, очень тяжело найти хорошую работу, с достойной заработной платой. Не отчаивайтесь, посмотрите в наших базах. Мы собираем только лучшие вакансии, на рынке труда России.

    [URL=]Работа в маскве[/URL] [URL=]Работа сварщиком в москве[/URL] [URL=]Работа для иногородних в москве[/URL] [URL=]Предложение вакансий[/URL] [URL=]Работа сегодня вакансии[/URL] [URL=]работа для девушек в москве[/URL] [URL=]Работа в королеве мытищах[/URL] [URL=]Вакансии вахтовым методом в москве[/URL] [URL=]Работа север вахта[/URL] [URL=]Работа супервайзер[/URL]

    Не откладывайте на завтра то, что можно сделать сегодня. Трудись как муравей, если хочешь быть уподоблен пчеле. Бездельник не имеет право сказать, что живет. Ничто в жизни не достается без труда. Для бездельника жизнь скучна, а для делового – радость. Только труд дает право на наслаждение. Труд – основа жизни. Трудолюбивая пчела умеет собрать мед и с горьких цветов.

    [URL=]Работа в королеве мытищах[/URL] [URL=]работа для девушек в москве[/URL] [URL=]Работа сегодня вакансии[/URL] [URL=]Работа сварщиком в москве[/URL] [URL=]Работа в маскве[/URL] [URL=]Предложение вакансий[/URL] [URL=]Работа север вахта[/URL] [URL=]Работа для иногородних в москве[/URL] [URL=]Работа супервайзер[/URL] [URL=]Вакансии вахтовым методом в москве[/URL]

  51. Player Profiles | October 30, 2010 at 5:57 am | Permalink

    Best you could edit the page title The Reluctant Blogger : Using MKAnnotation, MKPinAnnotationView and creating a custom MKAnnotationView in an MKMapView to something more catching for your content you create. I enjoyed the the writing even sononetheless.

  52. Dario Rinks | July 25, 2011 at 7:00 pm | Permalink

    Nice job, it is a fantastic post. The information is very good to know!

  53. LeBzul | January 10, 2012 at 4:50 am | Permalink

    Thank !

    I think :
    _imageView.frame = CGRectMake(kBorder, kBorder, kWidth – 2 * kBorder, kWidth – 2 * kBorder);

    It’s :
    _imageView.frame = CGRectMake(kBorder, kBorder, kWidth – 2 * kBorder, kHeight – 2 * kBorder);

  54. Carly Rae Jepsen Sex Tape | July 24, 2012 at 1:32 pm | Permalink

    you|and allow you to} know how {much|great|really|, very

  55. maquanhong | November 22, 2013 at 3:53 am | Permalink

    Thanks very much,I have trapped in the question which is I can’t add a button on BMKAnnotationView(baidu map) for a lone time. Final,I found the answer here.

  56. Ali | February 21, 2014 at 10:10 am | Permalink

    Thank you very much this was very helping.

{ 2 } Trackbacks

  1. […] I have an updated post here that builds on this project and also discusses using Using MKAnnotation,… […]

  2. […] I have an updated post here that builds on this project and also discusses using Using MKAnnotation,… […]

Post a Comment

Your email is never published nor shared. Required fields are marked *