31

I am trying to capture tap event on my MKMapView, this way I can drop a MKPinAnnotation on the point where user tapped. Basically I have a map overlayed with MKOverlayViews (an overlay showing a building) and I would like to give user more information about that Overlay when they tap on it by dropping a MKPinAnnotaion and showing more information in the callout. Thank you.

Pramod More
  • 1,092
  • 2
  • 16
  • 47
sbkb
  • 331
  • 1
  • 3
  • 4

4 Answers4

59

You can use a UIGestureRecognizer to detect touches on the map view.

Instead of a single tap, however, I would suggest looking for a double tap (UITapGestureRecognizer) or a long press (UILongPressGestureRecognizer). A single tap might interfere with the user trying to single tap on the pin or callout itself.

In the place where you setup the map view (in viewDidLoad for example), attach the gesture recognizer to the map view:

UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] 
    initWithTarget:self action:@selector(handleGesture:)];
tgr.numberOfTapsRequired = 2;
tgr.numberOfTouchesRequired = 1;
[mapView addGestureRecognizer:tgr];
[tgr release];

or to use a long press:

UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] 
    initWithTarget:self action:@selector(handleGesture:)];
lpgr.minimumPressDuration = 2.0;  //user must press for 2 seconds
[mapView addGestureRecognizer:lpgr];
[lpgr release];


In the handleGesture: method:

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
        return;

    CGPoint touchPoint = [gestureRecognizer locationInView:mapView];
    CLLocationCoordinate2D touchMapCoordinate = 
        [mapView convertPoint:touchPoint toCoordinateFromView:mapView];

    MKPointAnnotation *pa = [[MKPointAnnotation alloc] init];
    pa.coordinate = touchMapCoordinate;
    pa.title = @"Hello";
    [mapView addAnnotation:pa];
    [pa release];
}
Paras Joshi
  • 19,954
  • 11
  • 54
  • 69
  • Thank you for the advice, once I have it working I'll reply asap. I did try single tap but after that I was not able to show callouts of my PinAnnotations. Looks like I need to use LongPressureGesture – sbkb Dec 02 '10 at 16:09
  • 4
    The UITapGestureRecognizer does not recognize on MKMapView anymore in iOS 6. It worked in iOS 5. Any ideas on this problem? – Felix Oct 10 '12 at 19:18
  • 6
    @phix23, Try implementing `shouldRecognizeSimultaneouslyWithGestureRecognizer` and return `YES` from there. Will need to do `tgr.delegate = self;` before adding the GR for that shoudRecognize delegate method to be called. –  Oct 10 '12 at 19:30
  • Any thoughts on the "Legal" link (not sure if this even existed at the time this question was asked)? I'd like to allow that to behave as normal, and I'd like something more clever than iterating subviews of the map view to see if the touch was inside a label or whatever it is. – dokkaebi Nov 05 '13 at 18:45
  • add the UIPanGestureRecognizer too to handle zoom (pinch) and move (pan) – Yasmin Tiomkin Aug 14 '14 at 09:38
5

I setup a long press (UILongPressGestureRecognizer) in viewDidLoad: but it just detect the only one touch from the first.

Where can i setup a long press to detect all touch? (it means the map ready everytime waiting user touch to screen to push a pin)

The viewDidLoad: method!

- (void)viewDidLoad {
    [super viewDidLoad];mapView.mapType = MKMapTypeStandard;

    UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
    [self.mapView addGestureRecognizer:longPressGesture];
    [longPressGesture release];

    mapAnnotations = [[NSMutableArray alloc] init];
    MyLocation *location = [[MyLocation alloc] init];
    [mapAnnotations addObject:location];

    [self gotoLocation];
    [self.mapView addAnnotations:self.mapAnnotations];
}

and the handleLongPressGesture method:

-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
    // This is important if you only want to receive one tap and hold event
    if (sender.state == UIGestureRecognizerStateEnded)
    {NSLog(@"Released!");
        [self.mapView removeGestureRecognizer:sender];
    }
    else
    {
        // Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
        CGPoint point = [sender locationInView:self.mapView];
        CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
        // Then all you have to do is create the annotation and add it to the map
        MyLocation *dropPin = [[MyLocation alloc] init];
        dropPin.latitude = [NSNumber numberWithDouble:locCoord.latitude];
        dropPin.longitude = [NSNumber numberWithDouble:locCoord.longitude];
//        [self.mapView addAnnotation:dropPin];
        [mapAnnotations addObject:dropPin];
        [dropPin release];
        NSLog(@"Hold!!");
        NSLog(@"Count: %d", [mapAnnotations count]);
    }   
}
Paras Joshi
  • 19,954
  • 11
  • 54
  • 69
o0oKodako0o
  • 177
  • 3
  • 9
2

If you want to use a single click/tap in the map view, here's a snippet of code I'm using. (Cocoa and Swift)

let gr = NSClickGestureRecognizer(target: self, action: "createPoint:")
gr.numberOfClicksRequired = 1
gr.delaysPrimaryMouseButtonEvents = false // allows +/- button press
gr.delegate = self
map.addGestureRecognizer(gr)

in the gesture delegate method, a simple test to prefer the double-tap gesture …

func gestureRecognizer(gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: NSGestureRecognizer) -> Bool {
  let other = otherGestureRecognizer as? NSClickGestureRecognizer
  if (other?.numberOfClicksRequired > 1) {
    return true; // allows double click
  }

  return false
}

you could also filter the gesture in other delegate methods if you wanted the Map to be in various "states", one of which allowed the single tap/click

bshirley
  • 7,674
  • 1
  • 31
  • 38
0

For some reason, the UIGestureRecognizer just didn't work for me in Swift. When I use the UIGestureRecognizer way. When I used the touchesEnded method, it returns a MKNewAnnotationContainerView. It seems that this MKNewAnnotationContainerView blocked my MKMapView. Fortunately enough, it's a subview of MKMapView. So I looped through MKNewAnnotationContainerView's superviews till self.view to get the MKMapView. And I managed to pin the mapView by tapping.

Swift 4.1

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    let t = touches.first
    print(t?.location(in: self.view) as Any)
    print(t?.view?.superview?.superview.self as Any)
    print(mapView.self as Any)
    var tempView = t?.view
    while tempView != self.view {
        if tempView != mapView {
            tempView = tempView?.superview!
        }else if tempView == mapView{
            break
        }

    }
  let convertedCoor = mapView.convert((t?.location(in: mapView))!, toCoordinateFrom: mapView)
   let pin =  MKPointAnnotation()
    pin.coordinate = convertedCoor
    mapView.addAnnotation(pin)
}
William Tong
  • 371
  • 3
  • 12