25

How do I detect a single tap on an instance of MKMapView? Do I have to subclass MKMapView and then override the touchesEnded method?

Thanks,

-Chris

Juan Boero
  • 6,281
  • 1
  • 44
  • 62
ChrisJF
  • 6,822
  • 4
  • 36
  • 41

8 Answers8

36

If you're just looking to get notified of tap gestures without affecting any of the other touch behavior of the map, you'll want to use a UITapGestureRecognizer. It's super simple, just put in some code like this.

UITapGestureRecognizer* tapRec = [[UITapGestureRecognizer alloc] 
   initWithTarget:self action:@selector(didTapMap:)];
[theMKMapView addGestureRecognizer:tapRec];
[tapRec release];

That will call the didTapMap whenever theMKMapView receives a tap gesture and all the pinching, and dragging gestures will still work as they did before.

Paras Joshi
  • 20,427
  • 11
  • 57
  • 70
pseudopeach
  • 1,475
  • 14
  • 29
  • For me, setting a selector didn't work, but worked when using delegate functions (gestureRecognizerShouldBegin:) BTW, although Tap and Pan work, UIPinchGestureRecognizer doesn't work, not even using delegate functions. – Fede Mika May 23 '12 at 15:04
  • I'm not sure what to tell you without any more specifics than that... Using a selector always works for me. – pseudopeach May 24 '12 at 20:41
  • 1
    a colon is missing, ir should be `@selector(didTapMap:)` – Ali Jun 25 '12 at 00:03
  • This gesture recognizer also fires if the user double taps to zoom the map or if an annotation is selected or deselected. Usually not what you want. – Ortwin Gentz Feb 14 '13 at 18:37
3

Or depending on what you are trying to do, add an MKAnnotation (push pin, with a callout), so you have something to tap on - and then your map delegate will receive an event eg.

mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control

Paras Joshi
  • 20,427
  • 11
  • 57
  • 70
TimM
  • 293
  • 1
  • 3
  • 12
3

Hope this will help : How to intercept touches events on a MKMapView or UIWebView objects?

Community
  • 1
  • 1
tt.Kilew
  • 5,954
  • 2
  • 33
  • 51
  • 1
    Yes, if you read in the comments there is another solution. There is actually a third way of doing this (besides sub-classing MKMapView or layering your own view on top and passing on the touches to MKMapView). Instead of subclassing MKMapView, subclass the main window and pass the touch events to both MKMapView and your view. http://stackoverflow.com/questions/1121889/intercepting-hijacking-iphone-touch-events-for-mkmapview/1298330 Haven't tried this yet but apparently performance is better and pinch to zoom still works. – ChrisJF Oct 22 '10 at 13:34
  • this link is no longer valid – Pizzaiola Gorgonzola Jun 04 '13 at 11:43
3

Working Perfectly on iOS 8

- (void)viewDidLoad 
    {
        [super viewDidLoad];

        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:nil];
        doubleTap.numberOfTapsRequired = 2;
        doubleTap.numberOfTouchesRequired = 1;
        [self.mapView addGestureRecognizer:doubleTap];

        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
        singleTap.numberOfTapsRequired = 1;
        singleTap.numberOfTouchesRequired = 1;
        [singleTap requireGestureRecognizerToFail: doubleTap];
        [self.mapView addGestureRecognizer:singleTap];
     }

   - (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
     {
            if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
                return;
            //Do your work ...
     }
James P
  • 4,786
  • 2
  • 35
  • 52
Sanjeev Rao
  • 2,247
  • 1
  • 19
  • 18
0

You cant at this time intercept touches on a map view, you can try layering an opaque view on there and see if it picks up touches...

Daniel
  • 22,363
  • 9
  • 64
  • 71
0

Just add some code snippet as illustration of @tt-kilew answer. In my case, I want to point the user to itself on the map but do not want to interrupt his drag touch.

@interface PrettyViewController () <MKMapViewDelegate>

@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (assign, nonatomic) BOOL userTouchTheMap;

@end

@implementation PrettyViewController

#pragma mark - UIResponder

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];

    self.userTouchTheMap = [[touches anyObject].view isEqual:self.mapView];
}


#pragma mark - MKMapViewDelegate

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
    //We just positioning to user
    if (!self.userTouchTheMap) {
        CLLocationDistance radius = 5000;
        [self.mapView setRegion:MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 2*radius, 2*radius) animated:YES];
    }
}

@end
WINSergey
  • 1,977
  • 27
  • 39
0

Nothing I ever found worked, but I came up with this unperfect solution : In viewDidLoad

let singleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onMapClicked))
singleTapRecognizer.delegate = self
mapView.addGestureRecognizer(singleTapRecognizer)

In delegate :

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view!.frame.equalTo(mapView.frame)
}
Livio
  • 700
  • 6
  • 9
0

my 2 cents for swit 5.x:

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let v = touch.view
        let ssv = v?.superview?.superview
        if ssv === self.mapView{
            searchBar.resignFirstResponder()
        }
    }
}

it works. but honestly can break if apple changes layers of views. better a recognizer.

ingconti
  • 10,876
  • 3
  • 61
  • 48