How-To: Detect when MKAnnotation, MKAnnotationView is selected
I’m using MapKit to display a satellite map in one of my apps. I create custom annotations – and it all works great. However I wanted to be able to play sounds when a user touches one of my MKAnnotation’s on my MapKit MKMapView so that the sound matches the display of the callout (and when it disappears too).
There is no delegate method or other built-in mechanism for detecting when your annotation is selected, however it does have a selected property. So how to detect when the MKAnnotation is selected and then play my sounds? The answer is key/value observing.
Setup an observer on our imageAnnontationView:
// At the top of the .m file put: static NSString* const GMAP_ANNOTATION_SELECTED = @"gMapAnnontationSelected";
// Then later somewhere in your code, add the observer [imageAnnotationView addObserver:self forKeyPath:@"selected" options:NSKeyValueObservingOptionNew context:GMAP_ANNOTATION_SELECTED];
Then we get a callback whenever the selected property of our annotation changes:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{
NSString *action = (NSString*)context;
if([action isEqualToString:GMAP_ANNOTATION_SELECTED]){
BOOL annotationAppeared = [[change valueForKey:@"new"] boolValue];
// do something
}
}
The value of annotationAppeared will change based on the state of the annontations selected property. GMAP_ANOTATION_SELECTED is a constant string I set at the top of my file.
I’ve updated my post to make the GMAP_ANOTATION_SELECTED constant more obvious. Hopefully this helps people use the snippet above.

July 17th, 2009 at 6:51 am
This is exactly what I’m looking for, but I get errors when I add this to my MapViewController.
error: GMAP_ANNOTATION_SELECTED undeclared (first use in this function)
Any ideas?
July 17th, 2009 at 11:46 pm
oh, just re-read your post again, looks like you covered that GMAP_ANNOTATION_SELECTED is a constant you defined. Mind showing that definition? It would be really helpful, thanks!
July 18th, 2009 at 10:09 pm
Hubert, I had the same problem but I think I solved it.
You declare a constant string like this:
//this should be before your implementation e.g after the #imports.
extern NSString *const GMAP_ANNOTATION_SELECTED;
// This is after @implementation
NSString *const GMAP_ANNOTATION_SELECTED = @”ANSELECTED”;
July 21st, 2009 at 9:48 am
This is a really nifty idea. I was hoping to use to monitor the region property of my MKMapView but I have so far been unsuccessful. Is there any reason anyone can think of why the MKMapView would not fire observer events?
Thanks,
Paul
August 1st, 2009 at 5:54 am
So I found a MUCH easier way to do this:
What I did was create an annotation view using the MKPinAnnotationView (to continue using the pin graphics), and in that class you can override the method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
Here’s what the .h file looks like:
———–MyAnnotation.h—————–
@interface MyAnnotation : MKPinAnnotationView {
//stuff here
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
@end
——————————————
With this you can put whatever you want in that touchesBegan method. Here’s where I found the info:
http://developer.apple.com/iphone/library/documentation/MapKit/Reference/MKAnnotationView_Class/Reference/Reference.html
and I clicked under “inherits from” to UIView to find that method. http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/cl/UIView
hope this helps
August 17th, 2009 at 9:05 am
touchesBegan is ok when you just want to detect when an annotation is selected. However, if you want to also detect when it is deselected (by tapping on the surrounding area, for example), you need the observer above.
October 25th, 2009 at 6:35 am
I cannot get this to work at all, is it OBJ-C for iPhone? I get errors for your extern line, the const line and even more for the adding the observer?
October 25th, 2009 at 4:43 pm
I’m having a problem with this. If i have more than 1 pin on a map and the user selects a pin i display a subview over the top of my Map. then, when the user clicks the map all goes as planned and the subview disappears, BUT if a user clicks another pin, it fires the Observer 2 times. The first for the new selected pin and the second for the deselected pin. Is there anyway to get it to fire in the reverse order? or get it to fire only 1 time?
Thanks
November 2nd, 2009 at 7:38 am
[...] solution (almost) might be to use a property change listener for the selected value. Unfortunately, the selected property is only changed if you have a title [...]
November 18th, 2009 at 5:39 am
Hi, Thanks for the tip but i am having this problem,
i used it to show several annotation images on map and when user touchs on one of it, it opens another view but when i go back and touch the same annotation it does not open the view again, it opens when i touch it after clicking on the map or another annotation.
i tried to call [ann setSelected:NO animated:NO]; in this method but no way.
I want to use it because i dont wantto show callout just the image and touch it.
i tried it workaroud it by removing the annotation and adding it again, but at 3.1.2 SDK removing annotation releases it so it crashes.
Do you have a solution for the problem.
Thanks
December 6th, 2009 at 11:21 pm
[...] is a combination of the property change listener solution and moving the calloutOffset off the [...]
March 10th, 2010 at 2:01 am
Great post man ! Thank’s a lot.
I’ve seen many ways on the net to do this, all very very more complicated!
Thank you Very much.
April 28th, 2010 at 11:36 am
very handy, works well. thanks!!
June 28th, 2010 at 8:13 pm
James I have the same problem,
Did you resolved it?
August 13th, 2010 at 2:04 am
@joseph
I managed to fix this problem by calling deselectAnnotation on the MKMapView in observeValueForKeyPath. My code looks like this :
MKAnnotationView *view = (MKAnnotationView *)object;
[mapView deselectAnnotation:view.annotation animated:NO];