Custom callout bubble in MKMapView, final solution!

Custom callout bubble in MKMapView, final solution!

http://www.jakeri.net/2009/12/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];
}
}
}

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

This entry was posted in Development, iphone and tagged callout bubble, google maps, iphone, mkannotation, mkannotationview, mkmapview, property change listener. Bookmark the permalink.

39 Responses to Custom callout bubble in MKMapView, final solution!

  1. Pingback: Custom callout bubble to MKMapView in iPhone « JAKERI

  2. Craig says:

    Can you show how to get detailed information into the call-out? please?

  3. admin says:

    The custom callout is designed in Interface Builder. You may design this however you want. Add detailed information to your custom MKAnnotation class. Did you test the example?

    Not completely sure what you mean.

  4. Craig says:

    I tested your example. Very nice. I have been struggling for some time on how to pass additional properties to a call-out bubble such as phone, zip, notes, etc. The solution continues to elude me.

  5. Craig says:

    I got it working following your model. Again, a really, really nice solution.

  6. Craig says:

    How did you solve showing which annotation pin was in the selected state?

  7. admin says:

    Method observeValueForKeyPath ofObject is the surrounding annotation view. Calling annotationView.annotation will get your custom MKAnnotation implementation.

    See code in example.

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

  8. Craig says:

    no, I got that part. But all the annotation images are identical. It is impossible to tell “which” annotation is the selected one.

  9. admin says:

    Have not tested that. I solved this issue by moving map region to the center of the display when you click on it.

    It should probably be solvable with a layer above the mapview where you draw something around the annotation.

    It might also be possible to replace the image (MKAnnotationView.image) in the showAnnotation method in the code above. I have not tested it.

  10. admin says:

    It works. See below. Added a change to MKAnnotationView.image in th observeValueForKeyPath.


    - (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) {
    NSLog(@"annotation selected %@", ((MyAnnotationView*) object).annotation.title);
    [self showAnnotation:((MyAnnotationView*) object).annotation];
    ((MyAnnotationView*) object).image = [UIImage imageNamed:@"icon-sel.png"];
    }
    else {
    NSLog(@"annotation deselected %@", ((MyAnnotationView*) object).annotation.title);
    [self hideAnnotation];
    ((MyAnnotationView*) object).image = [UIImage imageNamed:@"icon.png"];
    }
    }
    }

  11. Craig says:

    That worked perfectly. I even added code to (showAnnotation) to center the annotation when clicked.

    I have a new question: suppose you wanted to show an “expanded” detail view linked from the small annotation view that pops up. I added a “more info” button to the small annotation overlay and linked to a new View Controller. But how do you transfer the annotation data to the next view controller?

    I have not seen another post out there like this one. It is very useful.

  12. Matthew says:

    Wondering how you might be able to customize the pins. Standard customization does not seem to be working for me.

    Also, wondering like @Craig, above, how you might push a new view controller from the annotation view.

    tia

  13. admin says:

    Sorry for my late answer.

    Just pass a reference to your MKAnnotation (set a property) containing information to the newly created view controller before you present it.

    I am not sure I understand what you mean by customize the pins? use another image?
    Then follow standard code from apple using a custom MKAnnotationView.

  14. Matthew says:

    I have tried countless ways at this point to change the pin, which works in my standard maps, but does not seem to do anything to change the pins in this demo.

    It is a great technique – thanks for sharing.

  15. admin says:

    I updated post with an example for you. Custom MKAnnotationView

  16. Matthew says:

    Awesome. Thanks for taking the time to help me out and share your knowledge.

  17. Sonali says:

    Very nice post, works very well.
    Thanks

  18. Matthew says:

    Do you have any other leads on learning more about KVO in relation to mapkit; pulling detailed information and passing it onto other views? Not really gleaning that from the Apple docs – perhaps it is just me

  19. Ujjwal says:

    Thanks a ton Jacob for this code. It was real help.

    I integrated your code with some existing one and am stuck at one issue – can you please take a look at

    http://stackoverflow.com/questions/2289731/iphone-warning-in-cyclic-import-in-mkannotation-method-not-found

    Basically I am trying to add a disclosure button to MoreInfoView class and on click push another view in the nav controller.

    I’d really appreciate your help. Thanks a lot in advance.

  20. Sijo says:

    Thanks for this great post.. i just need to add more details in MKPinAnnoataionView like Title,Address,Description,date, an image etc.. But its View should look like default MKPinannotation. I need that default black transparent bubble with the details…can we do anything in default MKPinannoation ? something like rightCalloutAccessoryView or leftCalloutAccessoryView ? .. but when am using this it getting located at left or Right side of PIn..thanks in advance

  21. John says:

    I did not see any code to forward pinch-zoom event from your TouchView to your map view. We’re you able to get pinch zoom to work on your TouchView, so that you can zoom your MKMapView? Thanks

  22. admin says:

    Yes. Everything works as the built in solution.
    TouchView is only used to catch deselection of tbe custom annotation. The original annotationview is actually visible if you have a really large iphone screen.

    CGPoint notNear = CGPointMake(10000.0,10000.0);
    self.calloutOffset = notNear;

  23. John says:

    Cool. My soln almost works. I’ll adapt to yours and see how that works out. Thanks for sharing.

  24. Chris says:

    When i try this with OS4 the map View goes crazy to the point 10000.0,10000.0 where the original annotationview is.

    It only works when i try this:
    CGPoint notNear = CGPointMake(1.0,1.0);
    self.calloutOffset = notNear;

    But then i have both annonationviews on screen.

    Do you have any solution for this? I only have an iPod touch to test and i can’t go back from beta.

  25. admin says:

    I have not have the time to test in OS4 yet. If you have any solution please mail me.
    OS4 is still in NDA so I can not say to much.

  26. Lifey says:

    There seems to be a problem with your solution on iPhoneOS3 simulator (and therefore most likely on the iPhoneOS3 device, though I haven’t checked): If the annotation is held for around a second or two, it zooms to the location of the callout bubble (which in your example is quite far away!) Do you have any ideas on how to remedy this issue?

  27. admin says:

    I did not know that. Have to take a look at it.

  28. admin says:

    I think I have found a solution working for iPhoneOS 3.0, 3.1, 3.2 and 4.0. Both iPad and iPhone. Will try to make new post in a couple of days.

  29. dc says:

    Have you any idea of a way of releasing the observationInfo so that it does not leak when an annotation is removed?

    Much thanks for the sample code.

  30. adam says:

    I love your solution for this, elegant and cunning.

    Really disappointed about the news that it’s not working on OS 4 beta, though. I guessed that might happen (Apple often does things like this – if you’ve got something visible, sooner or later they “helpfully” change the system behaviour to show it).

    Did you find a new solution (version 3 of this fix, I guess ) ?

  31. admin says:

    I think so. Will try to post tomorrow. Only thing I have not fixed is the leaking of observationinfo.

  32. Pingback: Revisited – Custom callout bubble in MKMapView, final solution! « JAKERI

  33. Devin Foley says:

    I was able to get this working in iOS4 by commenting out the notNear stuff and setting canShowCallout to NO:

    //CGPoint notNear = CGPointMake(10000.0,10000.0);
    //annView.calloutOffset = notNear;

    [annotationView setCanShowCallout:NO];

  34. admin says:

    Look at my latest post instead.

  35. Offbeat Attractions says:

    This is AWESOME. I’ve been trying to add an image on top of the marker which wasn’t working – until I found this. Thanks for sharing the code. It works perfectly

  36. Malek says:

    Hi, please, have you any tutorial which shows how to move data from a view to another to show them when i select the UIButtonTypeDetailDisclosure button of the pin ? my purpose is that when i click the detail button on the pin, i will be redirect to another view in which i place all details of the pin selected, please help, i really need step-by step tutorial,i have maked the pins with the detail button, and the data are retrieved from web-service, but i don’t know how to tell view “here the data of this pin, display it”.

  37. ambu.sangoli says:

    2011-05-22 01:14:43.415[6106:307] stopFollowLocation called. Good place to put stop follow location annotation code.
    2011-05-22 01:14:43.420[6106:307] stopFollowLocation called. Good place to put stop follow location annotation code.
    2011-05-22 01:14:43.424[6106:307] stopFollowLocation called. Good place to put stop follow location annotation code.
    2011-05-22 01:14:43.885[6106:307] annotation deselected (null)

    Please help i am getting all working but not showing any text and in console the above it is showing

  38. Simon Burfield says:

    Hello there, I downloaded by examples and ran it up on 4.2 in simulator and it does some very odd things when you click the pin and I end up with different views show?

你可能感兴趣的:(apple,object,iPhone,action,button,2010)