JAKERI

Icon

Consulting/Development in Java, Objective-C for web based systems and iPhone

Revisited – Custom callout bubble in MKMapView, final solution!

I thought I had the final solution for custom callout bubbles in MKMapView but I was wrong. There were a couple of errors found after testing.

  • If you press and hold it moves focus to the real callout bubble offset 10000px right and 10000px down
  • It did not work on the iPad (iPhoneOS 3.2) and iPhoneOS 4.0
  • There is a leak on the property observer

My latest solution solves the first two but not the third one. It works with iPhoneOS 3.0, 3.1, 3.2 and 4.0. I have only tested in simulator for 3.2 and 4.0.

To make it a easier to look at the code I have created my first github repository. You can alsoe download source for master (zip or tar) directly from github.

Update: I got my application, Gasmacken approved (2010-05-23) with this solution.

Custom callout bubble in MKMapView, final solution!

Once again, this post has been updated.

I was not completely satisfied with my prior solution to custom callout bubble in MKMapView due a drawback.

One drawback is that if you click an annotation in the TouchView it is not propagated down to the MKMapView which makes pinch zoom bit more tricky if you have many annotations. Someone out there might have a good solution for it?

Fortunately I figured out a new solution for the problem! A much more simple solution too.

It is a combination of the property change listener solution and moving the calloutOffset off the display.

Set the calloutOffset off the display and add an observer to the selected-property.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {

MKAnnotationView* annotationView = nil;

MyAnnotation *myAnnotation = (MyAnnotation*) annotation;
NSString* identifier = @"Pin";
MKPinAnnotationView* annView = (MKPinAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];

if(nil == annView) {
annView = [[[MKPinAnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier] autorelease];
}
//Add an observer for the selected-property on the MKAnnotationView. Delegate to self.
[annView addObserver:self
forKeyPath:@"selected"
options:NSKeyValueObservingOptionNew
context:GMAP_ANNOTATION_SELECTED];

[annView setPinColor:MKPinAnnotationColorGreen];

//Set calloutOffset off screen.
CGPoint notNear = CGPointMake(10000.0,10000.0);
annView.calloutOffset = notNear;
annotationView = annView;

[annotationView setEnabled:YES];
[annotationView setCanShowCallout:YES];

return annotationView;
}

Implement the observeValueForKeyPath method. It will be triggered when the property is selected or deselected.

- (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];
if (annotationAppeared) {
[self showAnnotation:((MyAnnotationView*) object).annotation];
}
else {
NSLog(@"annotation deselected %@", ((MyAnnotationView*) object).annotation.title);
[self hideAnnotation];
}
}
}

Screenshot_17

Take a look at my new example (with standard MKPinAnnotationView) or with custom annotation view.