NitWit Studios NitWit Studios - 4 months ago 10
iOS Question

How to add touch gesture to map but ignore touches on pins and annotations?

I have a mapview that uses

MKCircle
s to display radius information for certain user actions.

What I want to do, is allow the user to dismiss the
MKCircle
when they touch the map. However, I would like the
MKCircle
to NOT dismiss should the user touch any of the other pins or the
MKCircle
itself.

Any ideas?

Here is my current code, which dismisses the
MKCircle
when any part of the map is touched:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(deactivateAllRadars)];
[tap setCancelsTouchesInView:NO];
[_mapView addGestureRecognizer:tap];

Answer

In the deactivateAllRadars method, you can use hitTest:withEvent: to tell whether an MKAnnotationView has been tapped or not.

An example of this is shown in How can I catch tap on MapView and then pass it to default gesture recognizers? (it's the second code sample).

This will let you avoid removing the circle if an annotation has been tapped.

If an annotation has not been tapped, you can then check if an MKCircle was tapped by getting the coordinates of the touch (see How to capture Tap gesture on MKMapView for an example) and seeing if the distance from the touch to the circle's center is greater than its radius.

Note that the deactivateAllRadars should be changed to deactivateAllRadars:(UITapGestureRecognizer *)tgr because it will need information from the associated gesture recognizer. Also be sure to add a colon at the end of the method's selector where you alloc+init tap.

For example:

-(void)deactivateAllRadars:(UITapGestureRecognizer *)tgr
{
    CGPoint p = [tgr locationInView:mapView];

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

    id<MKAnnotation> ann = nil;

    if ([v isKindOfClass:[MKAnnotationView class]])
    {
        //annotation view was tapped, select it...
        ann = ((MKAnnotationView *)v).annotation;
        [mapView selectAnnotation:ann animated:YES];
    }
    else
    {
        //annotation view was not tapped, deselect if some ann is selected...
        if (mapView.selectedAnnotations.count != 0)
        {
            ann = [mapView.selectedAnnotations objectAtIndex:0];
            [mapView deselectAnnotation:ann animated:YES];
        }


        //remove circle overlay if it was not tapped...        
        if (mapView.overlays.count > 0)
        {
            CGPoint touchPoint = [tgr locationInView:mapView];

            CLLocationCoordinate2D touchMapCoordinate 
              = [mapView convertPoint:touchPoint toCoordinateFromView:mapView];

            CLLocation *touchLocation = [[CLLocation alloc] 
              initWithLatitude:touchMapCoordinate.latitude 
              longitude:touchMapCoordinate.longitude];

            CLLocation *circleLocation = [[CLLocation alloc] 
              initWithLatitude:circleCenterLatitude 
              longitude:circleCenterLongitude];

            CLLocationDistance distFromCircleCenter 
              = [touchLocation distanceFromLocation:circleLocation];

            if (distFromCircleCenter > circleRadius)
            {
                //tap was outside the circle, call removeOverlay...
            }
        }
    }
}