MKAnnotationView和点击检测

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

我现在想要添加一个MKAnnotationView到地图上。 我可以点击注解和mapView:mapView didSelectAnnotationView:view fires(这是我将添加额外的逻辑来显示UIView)。

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

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

有可能是一个更好,更干净的解决scheme,但一个办法就是利用hitTest:withEvent:在轻hitTest:withEvent:手势识别select器,例如

假设你已经添加了一个轻敲手势识别器到你的_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)

最后,我发现了另一种解决scheme,其缺点是仅适用于iOS> = 6.0 :实际上,此解决scheme利用新的-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer以这种方式添加到UIView

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

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

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

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

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

为什么不在viewForAnnotation中添加UITapGestureRecognazer,使用注解的reuseIdentifier来标识它是哪个注释,在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); } 

我不确定为什么你的地图视图上有一个UITapGestureRecognizer ,用纯文本表示,这显然意味着它会UITapGestureRecognizer地图的一些多点触控function。

我build议你看看和玩UIGestureRecognizercancelsTouchesInView属性(见文档 )。 我认为这可以解决你的问题。 确保你签出文件。

警告! 接受的解决scheme,也是下面的一个有时是有点bug。 为什么? 有时候你点击注释,但是你的代码就像你点击地图一样。 这是什么原因? 因为您在注释的框架周围点击了某处,例如+ – 1-6像素左右,但不在注释视图框内。

有趣的是,尽pipe你的代码会在这种情况下说“你点击地图,而不是注释”,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也接受接近触摸,不仅正确触摸):

我包括日志function,以便您可以在控制台中观看并了解问题。

 - (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.xp.x), (anView.frame.origin.yp.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; } 

我的注释框架有25×25大小,这就是为什么我接受30的距离。你可以像if(px> = anView.frame.origin.x – 6)&& Y等应用你的逻辑。