28

Im working with MKMapView and MKAnnotationView.

I have an annotation in the map. When the users tap on it, the callOut Bubble is displayed. When the annotation is tapped again ( and the callOut Bubble is visible ) i need to change to another view.

How can i detect the second tap, or the tap in the bubble?

Alejandro González
  • 638
  • 1
  • 8
  • 17
  • 3
    Simplest way is to set a button as the rightCalloutAccessoryView and implement calloutAccessoryControlTapped. Is that not sufficient or you must catch taps on the title and subtitle as well? –  Oct 12 '11 at 02:06

7 Answers7

12

Could you add a gesture recognizer when you're initializing the MKAnnotationView?

Here's the code for inside dequeueReusableAnnotationViewWithIdentifier:

UITapGestureRecognizer *tapGesture = 
        [[UITapGestureRecognizer alloc] initWithTarget:self 
                                        action:@selector(calloutTapped:)];
[theAnnotationView addGestureRecognizer:tapGesture];
[tapGesture release];

The method for the gesture recognizer:

-(void) calloutTapped:(id) sender { 
    // code to  display whatever is required next.

    // To get the annotation associated with the callout that caused this event:
    // id<MKAnnotation> annotation = ((MKAnnotationView*)sender.view).annotation;
}
Dolbz
  • 2,078
  • 1
  • 16
  • 25
11

Here's the swift version of Dhanu's answer, including getting data from the item selected to pass to the next view controller:

func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
    let gesture = UITapGestureRecognizer(target: self, action: #selector(MyMapViewController.calloutTapped(_:)))
    view.addGestureRecognizer(gesture)
}

func calloutTapped(sender:UITapGestureRecognizer) {
    guard let annotation = (sender.view as? MKAnnotationView)?.annotation as? MyAnnotation else { return }

    selectedLocation = annotation.myData
    performSegueWithIdentifier("mySegueIdentifier", sender: self)
}
Keith
  • 547
  • 6
  • 14
6

To tap the callout button after the user has clicked on the Annotation view, add a UITapGestureRecognizer in didSelectAnnotationView. This way you can implement tap on the callout without needing the accessory views.

You can then get the annotation object back from the sender for further action.

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self  action:@selector(calloutTapped:)];
    [view addGestureRecognizer:tapGesture];
}

-(void)calloutTapped:(UITapGestureRecognizer *) sender
{
    NSLog(@"Callout was tapped");

    MKAnnotationView *view = (MKAnnotationView*)sender.view;
    id <MKAnnotation> annotation = [view annotation];
    if ([annotation isKindOfClass:[MKPointAnnotation class]])
    {
        [self performSegueWithIdentifier:@"annotationDetailSegue" sender:annotation];
    }
}
Dhanu A
  • 207
  • 3
  • 1
  • 3
    Be careful with this solution. The problem here is that you create a tap gesture each time you select an annotation so you could have issues with multiple taps creation for example if you select it, deselect it and reselect it again. I think it's better adding a tap gesture at AnnotationView initialization or handling gesture removal at deselection – Beninho85 Sep 22 '16 at 18:01
5

Try to set custom image for button without changing UIButtonTypeDetailDisclosure type.

UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];        
[detailButton setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
3

Here is my solution to this question:

Swift 5

First, add MKMapViewDelegate method

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)

This gets called when annotation is selected and the callout bubble is shown. You can extract the callout bubble view like this, and add the tap gesture recognizer to it, like so:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    // Set action to callout view
    guard let calloutView = view.subviews.first else { return }
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(userDidTapAnnotationCalloutView(_:)))
    calloutView.addGestureRecognizer(tapGesture)
}

And handle tap action like this:

@objc
private func userDidTapAnnotationCalloutView(_ sender: UITapGestureRecognizer) {
    // Handle tap on callout here
}
Miki
  • 903
  • 7
  • 26
2

This works in Swift 5.2

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    let gesture = UITapGestureRecognizer(target: self, action: #selector(calloutTapped))
    view.addGestureRecognizer(gesture)
}

@objc func calloutTapped() {
    
    print ("Callout Tapped")
    
}
jat
  • 183
  • 3
  • 14
0

Swift 3, using On. You need to handle rightCalloutAccessoryView

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
  switch annotation {
  case let annotation as Annotation:
    let view: AnnotationView = mapView.dequeue(annotation: annotation)
    view.canShowCallout = true
    let button = UIButton(type: .detailDisclosure)
    button.on.tap { [weak self] in
      self?.handleTap(annotation: annotation)
    }
    view.rightCalloutAccessoryView = button
    return view
  default:
    return nil
  }
}
onmyway133
  • 45,645
  • 31
  • 257
  • 263