I have a list of annotations (MKPointAnnotation). I have a UIViewController which is for the whole view, MKMapView implementing Controller, which I thought is useful for detecting the users interaction with the map, my own MKPointAnnotation implementing (subclass) Controller which tells how to show the annotation.
However I'm struck at the detection of the tap event by the user.
Googling told me that I have to do something by implementing the following function.
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
and also that I have to implement this in some class which implements the MapViewDelegate (Protocol).
But I'm all confused and unable to move forward. Can anyone tell me where to do what?
Sorry for all the fuss!
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
{
[self performSegueWithIdentifier:@"DetailsIphone" sender:view];
}
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];
}
I include some screen snapshots for the user interface yielded by the above code in my answer here.
Robs example for Swift 3:
class MapViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
// Create map view in storyboard
view.delegate = self
}
}
extension MapViewController: MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "annotationView")
annotationView.canShowCallout = true
annotationView.rightCalloutAccessoryView = UIButton.init(type: UIButtonType.detailDisclosure)
return annotationView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
{
guard let annotation = view.annotation else
{
return
}
let urlString = "http://maps.apple.com/?sll=\(annotation.coordinate.latitude),\(annotation.coordinate.longitude)"
guard let url = URL(string: urlString) else
{
return
}
UIApplication.shared.openURL(url)
}
}
Add button in this method
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.canShowCallout = YES;
}
Then callout method
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
InfoView *infoView = [[InfoView alloc]initWithNibName:@"InfoView" bundle:nil];
[self.navigationController pushViewController:infoView animated:YES];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With