28
votes

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?

7
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?user467105

7 Answers

12
votes

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;
}
10
votes

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)
}
6
votes

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];
    }
}
5
votes

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

UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];        
[detailButton setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];
1
votes

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
}
1
votes

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")
    
}
0
votes

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
  }
}