How to get click event from a button added over MKAnnotationView

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;
}

Leave a Comment