9

我有一个MKMapView. 我UITapGestureRecognizer用一个水龙头添加了一个。

我现在想MKAnnotationView在地图上添加一个。我可以点击注释并mapView:mapView didSelectAnnotationView:view触发(我将在其中添加其他逻辑以显示 UIView)。

现在的问题是,当我点击注释时,MKMapView点击手势也会触发。

我可以设置它,如果我点击注释,它只会响应吗?

4

6 回答 6

6

可能有更好和更清洁的解决方案,但一种方法是 hitTest:withEvent:在点击手势识别选择器中利用,例如

假设您已将轻击手势识别器添加到您的_mapView

- (void)tapped:(UITapGestureRecognizer *)g
{
    CGPoint p = [g locationInView:_mapView];
    UIView *v = [_mapView hitTest:p withEvent:nil];

    if (v ==  subviewOfKindOfClass(_mapView, @"MKAnnotationContainerView"))
        NSLog(@"tap on the map"); //put your action here
}

// depth-first search
UIView *subviewOfKindOfClass(UIView *view, NSString *className)
{
    static UIView *resultView = nil;

    if ([view isKindOfClass:NSClassFromString(className)])
        return view;

    for (UIView *subv in [view subviews]) {
        if ((resultView = subviewOfKindOfClass(subv, className)) break;
    }
    return resultView;
}

它可能没有涵盖所有的边缘情况,但它似乎对我来说效果很好。

更新(iOS >= 6.0)

最后,我找到了另一种解决方案,它的缺点是仅对iOS >= 6.0有效:事实上,这种解决方案利用了新-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer添加到UIViews 的方式

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    // overrides the default value (YES) to have gestureRecognizer ignore the view
    return NO; 
}

即,从 iOS 6 开始,UIView在手势识别器应该忽略的每个视图中重写该方法就足够了。

于 2013-06-20T00:19:55.827 回答
4

您的解决方案应该- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch在您的委托上使用该方法。

在此方法中,您可以检查触摸是否在您的某个注释上,如果是,则不会激活return NO您的。gestureRecognizer

目标-C:

- (NSArray*)getTappedAnnotations:(UITouch*)touch
{
    NSMutableArray* tappedAnnotations = [NSMutableArray array];
    for(id<MKAnnotation> annotation in self.mapView.annotations) {
        MKAnnotationView* view = [self.mapView viewForAnnotation:annotation];
        CGPoint location = [touch locationInView:view];
        if(CGRectContainsPoint(view.bounds, location)) {
            [tappedAnnotations addObject:view];
        }
    }
    return tappedAnnotations;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return [self getTappedAnnotations:touch].count > 0;
}

迅速:

private func getTappedAnnotations(touch touch: UITouch) -> [MKAnnotationView] {
    var tappedAnnotations: [MKAnnotationView] = []
    for annotation in self.mapView.annotations {
        if let annotationView: MKAnnotationView = self.mapView.viewForAnnotation(annotation) {
            let annotationPoint = touch.locationInView(annotationView)
            if CGRectContainsPoint(annotationView.bounds, annotationPoint) {
                tappedAnnotations.append(annotationView)
            }
        }
    }
    return tappedAnnotations
}

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    return self.getTappedAnnotations(touch: touch).count > 0
}
于 2016-01-04T13:51:50.367 回答
2

为什么不直接在 viewForAnnotation 中添加 UITapGestureRecognizer,使用注解的重用标识符来识别它是哪个注解,在 tapGestureRecognizer 操作方法中您可以访问该标识符。

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    MKAnnotationView *ann = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"some id"];

    if (ann) {
        return ann;
    }

    ann = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"some id"];
    ann.enabled = YES;

    UITapGestureRecognizer *pinTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pinTapped:)];
    [ann addGestureRecognizer:pinTap];
}

-(IBAction)pinTapped:(UITapGestureRecognizer *)sender {
    MKAnnotationView *pin = (MKPinAnnotationView *)sender.view;
    NSLog(@"Pin with id %@ tapped", pin.reuseIdentifier);
}
于 2015-02-11T15:59:18.317 回答
1

警告!公认的解决方案以及下面的解决方案有时有点错误。为什么?有时您点击注释,但您的代码会像您点击地图一样。这是什么原因?因为您点击了注释框架周围的某个位置,例如在注释视图框架内但不在注释框架内的 +- 1-6 像素。

有趣的是,虽然您的代码在这种情况下会说“您点击了地图,而不是注释”,但 MKMapView 上的默认代码逻辑也将接受此关闭点击,就像它在注释区域中并会触发 didSelectAnnotation。

所以你必须在你的代码中反映这个问题。可以说这是默认代码:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:_customMapView];

    UIView *v = [_customMapView hitTest:p withEvent:nil];

    if (![v isKindOfClass:[MKAnnotationView class]])
    {
      return YES; // annotation was not tapped, let the recognizer method fire
    }

    return NO;
}

并且此代码还考虑了注释周围的一些接近触摸(因为如上所述,MKMapView 也接受接近触摸,而不仅仅是正确的触摸):

我包含了日志功能,因此您可以在控制台中观看它并了解问题。

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:_customMapView];
    NSLog(@"point %@", NSStringFromCGPoint(p));

    UIView *v = [_customMapView hitTest:p withEvent:nil];

    if (![v isKindOfClass:[MKAnnotationView class]])
    {
       // annotation was not tapped, be we will accept also some
       // proximity touches around the annotations rects

       for (id<MKAnnotation>annotation in _customMapView.annotations)
       {
           MKAnnotationView* anView = [_customMapView viewForAnnotation: annotation];

           double dist = hypot((anView.frame.origin.x-p.x), (anView.frame.origin.y-p.y)); // compute distance of two points
           NSLog(@"%@ %f %@", NSStringFromCGRect(anView.frame), dist, [annotation title]);
           if (dist <= 30) return NO; // it was close to some annotation se we believe annotation was tapped
       }
       return YES;
    }

    return NO;
}

我的注释框大小为 25x25,这就是我接受 30 距离的原因。您可以应用您的逻辑,例如 if (px >= anView.frame.origin.x - 6) && Y 等。

于 2016-08-15T13:32:03.523 回答
0

touch.view如果我们只测试手势委托中的超级视图会容易得多:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var tv = touch.view
    while let view = tv, !(view is MKAnnotationView) {
        tv = view.superview
    }
    return tv == nil
}
于 2018-10-31T02:31:24.250 回答
-1

我不确定为什么您UITapGestureRecognizer的地图视图中会有一个,用纯文本说这显然意味着它会弄乱您地图的一些多点触控功能。

我建议您看一下并使用(请参阅文档)的cancelsTouchesInView属性。我认为这可以解决您的问题。确保查看文档。UIGestureRecognizer

于 2013-06-19T21:36:05.140 回答