1

I am trying to make the callout bubble of a map annotation clickable(I want the title to be clickable). There's no good way to do this from what I've seen so I've implemented a gesture recognizer on the map so that I can do a hit test to determine if the callout has been tapped. It works fine most of the time, but sometimes the gesture recognizer fails to trigger.

Here is my code

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    UITapGestureRecognizer* calloutRecognizer = [[UITapGestureRecognizer alloc]
            initWithTarget:self action:@selector(calloutTapped:)];
    calloutRecognizer.cancelsTouchesInView = false;
    [self.mapView addGestureRecognizer:calloutRecognizer];
}


- (void)calloutTapped:(UITapGestureRecognizer *)gestureRecognizer
{
    CGPoint hitPoint = [gestureRecognizer locationInView:self.mapView];
    UIView *tappedView = [self.mapView hitTest:hitPoint withEvent:nil];
    // This passthrough button ends up consuming events in the callout
    // There seems to be no way to target it explicitly so we must check the class name of the view
    if([NSStringFromClass([tappedView class]) isEqualToString: @"_MKSmallCalloutPassthroughButton"]){
        if(self.mapView.selectedAnnotations.count > 0 ){
            [self clinicTappedForClinic:[self getClinicForAnnotation :self.mapView.selectedAnnotations[0]]];
        }
    }
}

I am testing using the iPhone 7 simulator

rykeeboy
  • 645
  • 2
  • 8
  • 22
  • are you trying to add button on map to want to do something on map click? – Sid Mhatre Mar 08 '17 at 19:29
  • I have some annotations that show a callout when they are tapped. I have views for both the left and right callout accessories. When you tap the right one it takes you to another screen but I would like to make it so that tapping the callout title also takes you to the same screen – rykeeboy Mar 08 '17 at 19:32
  • https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/Art/bullseye_view_2x.png This isn't my app but the image is very similar to what I am doing. I want the text to be clickable – rykeeboy Mar 08 '17 at 19:39
  • which annotations you talking about. Is it about marker info window? – Sid Mhatre Mar 08 '17 at 19:41
  • Yeah. That window is the callout for an MKAnnotationView(which is the pin on the map) – rykeeboy Mar 08 '17 at 19:48

2 Answers2

1

There are two ways of detecting user interaction with your annotation view. The common technique is to define a callout (that standard little popover bubble that you see when you tap on a pin in a typical maps app) for your MKAnnotationView. And you create the annotation view for your annotation in the standard viewForAnnotation method:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;

    MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
    annotationView.canShowCallout = YES;
    annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

    return annotationView;
}

By doing this, you get a callout, but you're adding an right accessory, which is, in my example above, a disclosure indicator. That way, they tap on your annotation view (in my example above, a pin on the map), they see the callout, and when they tap on that callout's right accessory (the little disclosure indicator in this example), your calloutAccessoryControlTapped is called (in my example below, performing a segue to some detail view controller):

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// tap action
}

That's a very typical user experience on the small iPhone screen.

But, if you don't like that UX and you don't want the standard callout, but rather you want something else to happen, you can define your MKAnnotationView so that a callout is not shown, but instead you intercept it and do something else (for example, on iPad maps apps, you might show some more sophisticated popover rather than the standard callout). For example, you could have your MKAnnotationView not show a callout:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;

    MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
    annotationView.canShowCallout = NO;

    return annotationView;
}

But you can then manually handle didSelectAnnotationView to detect when a user tapped on your MKAnnotationView, in this example showing a popover:

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    [mapView deselectAnnotation:view.annotation animated:YES];

    DetailsViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailsPopover"];
    controller.annotation = view.annotation;
    self.popover = [[UIPopoverController alloc] initWithContentViewController:controller];
    self.popover.delegate = self;
    [self.popover presentPopoverFromRect:view.frame
                                  inView:view.superview
                permittedArrowDirections:UIPopoverArrowDirectionAny
                                animated:YES];
}

If you trying using UITapGestureRecognizer then you can refer : How can I catch tap on MapView and then pass it to default gesture recognizers?

Community
  • 1
  • 1
Sid Mhatre
  • 3,272
  • 1
  • 19
  • 38
  • Thanks for the detailed response but it doesn't quite answer my question. I have implemented the standard callout with tappable accessory views. The problem is that I find the right callout accessory to be to small and want to be able to also tap the title in the callout. My implementation works generally but sometimes the gesture recognizer isn't triggered at all, meaning I can't check which view was tapped – rykeeboy Mar 08 '17 at 20:02
  • `if ([gestureRecognizer.view isKindOfClass:[MKAnnotationView class]]) ` add this to check – Sid Mhatre Mar 08 '17 at 20:09
  • It won't make a difference. The check is fine. The callout tapped method is sometimes not called for some unknown reason. It's an issue with the gesture recognizer not the annotation – rykeeboy Mar 08 '17 at 20:21
1

Add delegate to tapGestureRecognizer:

//add <UIGestureRecognizerDelegate> to .h 

//add this where you create tapGestureRecognizer...
tapGestureRecognizer.delegate = self;


// check tapGestureRecognizer working or not properly 
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
    shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}
Sid Mhatre
  • 3,272
  • 1
  • 19
  • 38