Search code examples
iphoneiostouchmkmapviewmkannotationview

Prevent touch propagation to annotation beneath


I have a custom MKAnnotationView. In its setselected:animated method I'm adding to it a custom bubble loaded from a nib, adjust the annotationview's frame to include this view and redraw the annotation circle with other color, like this (first - not selected , second - selected, blue - frame, green - custom bubble view with alpha = 0.8, red - the annotationview):

enter image description here

It works fine, the bubble appears, and can be "closed" only by tapping outside of it (this is why I've increased the frame). I have some buttons on this bubble and they are clickable if there is nothing under the annotation just the map.

BUT when under the callout bubble there is another annotation I can click "through" the entire bubble. When I tap on one of the buttons, the tap highlight appears, but an other annotation gets selected because the didSelectAnnotationView fires ...

I tried to make the bubble opaque/semitransparent, no luck; set exclusiveTouch on buttons, on the view itself, no luck; tried not to mess with frame, still can click through. Am I missing something ?

Thanks

Edit : Shorter: Why can I click through a UIView added in addSubview in an MKAnnotationView if there is other MKAnnotaionView under this UIView ?

Details :

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
  if(selected)
  {
    initialFrame = self.frame;       // save frame and offset to restore when deselected
    initialOffset = self.centerOffset;  // frame is correct for a circle, like {{2.35, 1.47}, {12, 12}}

    if (!self.customCallout) 
    {
      self.customCallout = [[[NSBundle mainBundle] loadNibNamed:@"CustomCallout" owner:self options:nil] objectAtIndex:0];
    }
    // adjust annotationview's frame and center
    // callout is 200x120, here frame is {{2.35, 1.47}, {200, 132}} 
    self.customCallout.layer.cornerRadius=5;
    self.customCallout.exclusiveTouch = YES;
    [self addSubview:self.customCallout];
  }
...
}

initWithAnnotation has these :

   self.canShowCallout = NO;  // to appear the subview
   self.exclusiveTouch = YES; // ...
   self.enabled = YES;
   self.opaque = YES;

Solution

  • The default behavior of the touch handling methods (touchesBegan: touchesEnded: etc.) have this note in the documentation:

    The default implementation of this method does nothing. However immediate UIKit subclasses of UIResponder, particularly UIView, forward the message up the responder chain.

    MKAnnotationView is a subclass of UIVIew. As a result, when your annotation gets a touch, it is passing it to it's super class and on up the responder chain, so eventually you map view gets the touch and activates the covered annotation.

    To resolve, implement the touch handling methods in your annotationView class, and do not pass the touch events up the responder chain.