Kevin Kevin - 6 months ago 25
iOS Question

Ignore mapView:didSelectAnnotationView when long press is occuring

I am struggling with this one. I have a mapview with many pins in a small area. I want to ignore the mapview's selecting of an annotation when I am long pressing on the map, regardless if i am longpressing on an annotation view. It seems as though the annotation is getting selected on a touchDown, rather than touch up inside on the annotation view, which is annoying.

I have added a longpress gesture to the mapview:

UILongPressGestureRecognizer *longRec = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(addPin:)];
longRec.cancelsTouchesInView = YES;
[self.mapView addGestureRecognizer:longRec];


This is not recognized when I long press over an annotation view. As soon as I press down the did select annotation delegation call is processed and the long press never fires.

I tried blocking the tap gesture recognizer which of course doesn't work because the mapview's gestures are not delegated to my map view controller, so this doesn't work:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
return NO;
}
return YES;
}


I also tried adding a longpress gesture to the annotation view as a hack, but that never gets fired either, and I don't like this strategy anyway.

Is there a way to block the mapview's annotation selection when a longpress gesture is pending on the mapview?

Answer

Figured out a solution. Basically subclassing MKMapView and implementing handleTap and handleLongPress.

In there I block a tap during a long press. I also give a little delay to handle the case where both gestures are simultaneously recognized:

@implementation KWMapView // :MKMapView

- (void)handleTap:(UITapGestureRecognizer *)tapGesture
{
    CGPoint tapPoint = [tapGesture locationInView:self];
    NSUInteger numberOfTouches = [tapGesture numberOfTouches];

    if (numberOfTouches == 1 && tapGesture.state == UIGestureRecognizerStateEnded) {
        if (!self.blockTap) {
            id v = [self hitTest:tapPoint withEvent:nil];

            if ([v isKindOfClass:[MKAnnotationView class]]) {
                [self addAnnotation:[v annotation]];
                [self selectAnnotation:[v annotation] animated:YES];
            } else {
                [[NSNotificationCenter defaultCenter] postNotificationName:PNMapViewDidTapMap object:tapGesture];
            }
        }
    }
}

- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
    self.blockTap = YES;

    if (sender.state == UIGestureRecognizerStateBegan) {
        [[NSNotificationCenter defaultCenter] postNotificationName:PNMapViewDropPinGesture object:sender];
    }

    if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) {
        double delayInSeconds = .2;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            self.blockTap = NO;
        });
    }
}
@end