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议你看看和玩UIGestureRecognizer
的cancelsTouchesInView
属性(见文档 )。 我认为这可以解决你的问题。 确保你签出文件。
警告! 接受的解决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等应用你的逻辑。