14

Anyone know if there's a way to get click event from a button that is added to MKAnnotationView, this button is used as label just to display the name of each pin on the map , now I successd to show a custom view (which contains image, text ....) when the pin is clicked so i need to do the same thing when the button (label) is clicked.

Thanks for any advice you can provide.

code for button in MKAnnotationView:

UIButton * pinButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 140, 28)];
[pinButton.titleLabel setTextColor:[UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1]]; 
[pinButton setCenter:CGPointMake(pinAnnotationView.center.x + 70, pinAnnotationView.center.y + 10)]; 
[pinButton addTarget:self action:@selector(pinLabelClicked) forControlEvents:UIControlEventTouchUpInside]; 
[pinAnnotationView addSubView:pinButton]; 
[pinButton setUserInteractionEnabled:YES];
Girish
  • 4,692
  • 4
  • 35
  • 55
Sultana
  • 153
  • 1
  • 1
  • 7

3 Answers3

16

The standard UI approach is to use the callout view and add an accessory button as progrmr shows.

However, if you must add a button directly to the MKAnnotationView, the problems with your approach are that the MKPinAnnotationView's default frame (which can't easily be changed) is smaller than the button you're adding so most of the button will not respond to touches and even if you switch to using an MKAnnotationView and increase the frame size, the MKMapView will prevent the button from getting any touches.

What you'll need to do is add a UITapGestureRecognizer to the button (use the gesture handler's action method instead of an addTarget on the button) and add the button to a plain MKAnnotationView with an appropriate frame size instead of an MKPinAnnotationView.

Example:

- (MKAnnotationView *)mapView:(MKMapView *)mapView 
        viewForAnnotation:(id<MKAnnotation>)annotation
{
    MKAnnotationView *annView = (MKAnnotationView *)[mapView 
            dequeueReusableAnnotationViewWithIdentifier: @"pin"];
    if (annView == nil)
    {
        annView = [[[MKAnnotationView alloc] initWithAnnotation:annotation 
                      reuseIdentifier:@"pin"] autorelease];

        annView.frame = CGRectMake(0, 0, 200, 50);

        UIButton *pinButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        pinButton.frame = CGRectMake(0, 0, 140, 28);
        pinButton.tag = 10;

        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] 
            initWithTarget:self action:@selector(handlePinButtonTap:)];
        tap.numberOfTapsRequired = 1;
        [pinButton addGestureRecognizer:tap];
        [tap release];

        [annView addSubview:pinButton]; 
    }

    annView.annotation = annotation;

    UIButton *pb = (UIButton *)[annView viewWithTag:10];
    [pb setTitle:annotation.title forState:UIControlStateNormal];

    return annView;
}

- (void) handlePinButtonTap:(UITapGestureRecognizer *)gestureRecognizer 
{
    UIButton *btn = (UIButton *) gestureRecognizer.view;
    MKAnnotationView *av = (MKAnnotationView *)[btn superview];
    id<MKAnnotation> ann = av.annotation;
    NSLog(@"handlePinButtonTap: ann.title=%@", ann.title);
}


Note that this will prevent the map view's didSelectAnnotationView delegate method from firing. If you need that method to fire (in addition to the button's gesture handler method), then add the following:

//in the view controller's interface:
@interface YourVC : UIViewController <UIGestureRecognizerDelegate>

//where the UITapGestureRecognizer is created:
tap.delegate = self;

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
        shouldRecognizeSimultaneouslyWithGestureRecognizer
            :(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
  • Thanks for your reply it's very useful, however i want just know how to add pins in this case ?? – Sultana Aug 04 '11 at 14:54
  • Not sure what you mean. The annotations can be added as usual by calling addAnnotation. Do you mean how to get the default "pin" image? –  Aug 04 '11 at 15:00
  • i'm talking about pins (icon) which must be connected to each button (label). – Sultana Aug 04 '11 at 15:08
  • In viewForAnnotation, you'll have to do `annView.image = your-UIImage;` (or you could create a UIImageView and addSubview to the annView) using your own images. [This answer](http://stackoverflow.com/questions/1185611/mkpinannotationview-are-there-more-than-three-colors-available/2066487#2066487) has some default pin images you can use. –  Aug 04 '11 at 15:13
  • Ok thanks very much now i understand what you mean, it's not worth using MKPinAnnotationView. – Sultana Aug 04 '11 at 15:24
  • sorry but when i used annView.image = myImage the button losed event click again – Sultana Aug 04 '11 at 16:10
  • it's work fine when i created a UIImageView and addSubview to the annView as you suggested. thank's again – Sultana Aug 04 '11 at 16:18
  • Now i have another problem related to this solution, the pins are misplaced on the map (shifted) and when i zoomIn/zoomOut they change places, plz any idea about this problem ? thanks again – Sultana Aug 11 '11 at 11:18
  • Don't do this. I added a tap gesture recognizer to the custom annotation view class, and pretty soon MKMapView stopped calling my delegate methods like regionDidChangeAnimated: and viewForAnnotation: randomly. – Anurag May 14 '12 at 18:09
  • @Anurag, thanks, were you able to find a solution or workaround? –  May 14 '12 at 18:20
  • @AnnaKarenina - I tried this approach from the Asynchrony Blog last year which worked for me - http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/. Their solution is quite complicated as it tries to work within the limitations of the map's internal workings. – Anurag May 14 '12 at 19:05
3

I added a button to the callout view (in my app) using this code (reduced for clarity):

-(MKAnnotationView *)mapView:(MKMapView *)map 
        viewForAnnotation:(StationItem*)annotation
{
    static NSString *AnnotationViewID = @"stationViewId";

    if ([annotation isKindOfClass:[MKUserLocation class]]) {
        return nil;  // use default user location view
    }

    MKPinAnnotationView *annotationView = 
        (MKPinAnnotationView*) [mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];

    if (annotationView == nil) {
        // if an existing pin view was not available, create one
        annotationView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID] autorelease];

        // add rightAccessoryView
        UIButton* aButton = [UIButton buttonWithFrame:CGRectMake(0, 0, 75, 30)];
        [aButton setTitle:@"Directions" forState:UIControlStateNormal];
        annotationView.rightCalloutAccessoryView = aButton;
    }

    annotationView.annotation = annotation;
    annotationView.canShowCallout = YES;
    annotationView.animatesDrop   = NO;

    return annotationView;
}

After doing this you need to implement the MKMapViewDelegate to handle callback to the delegate when the button is tapped:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
progrmr
  • 75,956
  • 16
  • 112
  • 147
  • thanks but i can't use rightCalloutAccessoryView because i disabled canShowCallout, to let the click on pin show just the custom view which i created. – Sultana Aug 04 '11 at 13:48
-1

try passing the touches to the subview:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 [pinButton touchesEnded:touches withEvent:event];
}

I'm surprised you have a problem though if your buttonView is in front? try making sure it is: [self bringSubviewToFront:pinButton];

Kampai
  • 22,848
  • 21
  • 95
  • 95
ader
  • 5,403
  • 1
  • 21
  • 26
  • 1
    the button apear near the pin but it's not clickable i've tried bringSubviewToFront but no result. – Sultana Aug 04 '11 at 13:28