Templar
Templar

Reputation: 1694

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;

Upvotes: 3

Views: 2388

Answers (4)

I had this issue recently, spending way to much time that I did not have. Solving it, actually ended up being quite straightforward, of course only realizing such after spending ages.

You'll simply want to subclass your MapView, and within that subclass to the following:

override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    debugPrint(gestureRecognizer)
    if gestureRecognizer is UITapGestureRecognizer {
        return true
    } else {
    return false
    }
}

The reason being, that all the handling of map interaction, such as zooming panning etc, seems to be handled internally, and not accessible to us. The gestures causing us problems is however accessible, but for my case, I only want the tap gesture that handles selecting and deselecting of an annotation.

Upvotes: 0

make.it.floss
make.it.floss

Reputation: 60

I think you will find the following links very useful:

http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-2/

How do I make a MKAnnotationView touch sensitive?

The first link discusses (among other things) how to prevent the propagation and the second one how to detect the touches.

Upvotes: 0

Chip Coons
Chip Coons

Reputation: 3201

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.

Upvotes: 3

Codebear
Codebear

Reputation: 196

can u check which event fire first

1, the button on bubble
2, didSelectAnnotationView

if the button on bubble fire first you can intersect the touch in bubble by subclassing the

touchesBegan:touches
touchesMove:touches
touchesEnd:touches

in bubble view to preventing the propagation

Upvotes: 0

Related Questions