Detect Tap on CalloutBubble in MKAnnotationView
Asked Answered
F

7

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?

Frady answered 3/8, 2010 at 10:48 Comment(1)
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?Leafstalk
T
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;
}
Touchstone answered 13/10, 2011 at 12:39 Comment(4)
The callout is not the same as the annotation view. The callout is the bubble that appears when you tap the callout view.Homy
I added the gestureRecognizer in the mapView:didSelectAnnotationView: method and this solution worked perfectly for me.Chasteen
@DanielT. You the MVP :PJointure
you need to use this: func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)Bollen
J
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)
}
Jeffryjeffy answered 9/6, 2016 at 12:9 Comment(0)
N
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];
    }
}
Nonoccurrence answered 6/11, 2014 at 1:23 Comment(1)
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 deselectionAlley
D
5

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

UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];        
[detailButton setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];
Distal answered 24/2, 2015 at 0:34 Comment(1)
Nice one! So sad that stuff like this is not documented anywhere in the APIs -.-Dopey
F
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
}
Flak answered 12/12, 2019 at 10:38 Comment(0)
A
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")
    
}
Ablebodied answered 29/10, 2020 at 13:13 Comment(0)
M
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
  }
}
Manille answered 10/7, 2017 at 21:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.