How to add 'Maps' app link to each of my map annotations

ios, ios6, iphone, xcode

Solution

Launching the Maps App of the Location Awareness Programming Guide says:

If you would prefer to display map information in the Maps app as opposed to your own app, you can launch Maps programmatically using one of two techniques:

In iOS 6 and later, use an `MKMapItem` object to open Maps. In iOS 5 and earlier, create and open a specially formatted map URL as described in Apple URL Scheme Reference. The preferred way to open the Maps app is to use the `MKMapItem` class. This class offers both the `openMapsWithItems:launchOptions:` class method and the `openInMapsWithLaunchOptions:` instance method for opening the app and displaying locations or directions.

For an example showing how to open the Maps app, see “Asking the Maps App to Display Directions.”

So, you should:

Make sure to define your view controller to be the `delegate` for your map view;

Write a `viewForAnnotation` that turns on `canShowCallout` and turns on the callout accessory view:

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

    MKAnnotationView* annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                                    reuseIdentifier:@"MyCustomAnnotation"];

    annotationView.canShowCallout = YES;
    annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

    return annotationView;
}

Then write a `calloutAccessoryControlTapped` method that opens the maps as outlined above, based upon what versions of iOS you're supporting, e.g., for iOS 6:

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    id <MKAnnotation> annotation = view.annotation;
    CLLocationCoordinate2D coordinate = [annotation coordinate];
    MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate addressDictionary:nil];
    MKMapItem *mapitem = [[MKMapItem alloc] initWithPlacemark:placemark];
    mapitem.name = annotation.title;
    [mapitem openInMapsWithLaunchOptions:nil];
}

I don't know what additional geographic information your have in your KML, but you can presumably fill in the `addressDictionary` as you see fit.

In answer to your follow-up question about how to use the `addressDictionary` parameter of the `MKPlacemark` initializer method, `initWithCoordinate`, if you had `NSString` variables for the street `address`, the `city`, the `state`, the `zip`, etc., it would look like:

NSDictionary *addressDictionary = @{(NSString *)kABPersonAddressStreetKey : street,
                                    (NSString *)kABPersonAddressCityKey   : city,
                                    (NSString *)kABPersonAddressStateKey  : state,
                                    (NSString *)kABPersonAddressZIPKey    : zip};

For this to work, you have to add the appropriate framework, `AddressBook.framework`, to your project and import the header in your .m file:

#import <AddressBook/AddressBook.h>

The real question, though, was how to set the `name` for the `MKMapItem` so it doesn't show up as "Unknown Location" in the maps app. That's as simple as setting the `name` property, probably just grabbing the `title` from your `annotation`:

mapitem.name = annotation.title;

Problem

There are a few tutorials and questions on this, but I'm not knowledgeable enough yet to understand how to implement them into my particular app. I get JSON annotation data from a URL and parse it and add each annotation in for loop. I want to add a link on each annotation to open Maps for directions. Here's my ViewController.H ``` #import <UIKit/UIKit.h> #import <MediaPlayer/MediaPlayer.h> #import <MapKit/MapKit.h> //MAP Setup @interface ViewController : UIViewController <MKMapViewDelegate> //map setup @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property (nonatomic, strong) NSMutableData *downloadData; //- (IBAction)refreshTapped:(id)sender; @end ``` and my ViewController.m ``` - (void)viewDidLoad { //////////////////////// //Connection to download JSON map info //////////////////////// self.downloadData = [NSMutableData new]; NSURL *requestURL2 = [NSURL URLWithString:@"http:OMITTED"]; NSURLRequest *request = [NSURLRequest requestWithURL:requestURL2]; NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self]; //scroller [scroller setScrollEnabled:YES]; [scroller setContentSize:CGSizeMake(320,900)]; [super viewDidLoad]; //Map [self.mapView.userLocation addObserver:self forKeyPath:@"location" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.downloadData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { id parsed = [NSJSONSerialization JSONObjectWithData:_downloadData options:kNilOptions error:nil]; //////////////////////// //Iterating and adding annotations //////////////////////// for (NSDictionary *pointInfo in parsed) { NSLog([pointInfo objectForKey:@"name"]); double xCoord = [(NSNumber*)[pointInfo objectForKey:@"lat"] doubleValue]; double yCoord = [(NSNumber*)[pointInfo objectForKey:@"lon"] doubleValue]; CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(xCoord, yCoord); MKPointAnnotation *point = [MKPointAnnotation new]; point.coordinate = coords; point.title = [pointInfo objectForKey:@"name"]; [self.mapView addAnnotation:point];// or whatever your map view's variable name is } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } //centers map on user loc and then allows for movement of map without re-centering on userlocation check. -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if ([self.mapView showsUserLocation]) { MKCoordinateRegion region; region.center = self.mapView.userLocation.coordinate; MKCoordinateSpan span; span.latitudeDelta = .50; // Change these values to change the zoom span.longitudeDelta = .50; region.span = span; [self.mapView setRegion:region animated:YES]; self.mapView.showsUserLocation = NO;} } - (void)dealloc { [self.mapView.userLocation removeObserver:self forKeyPath:@"location"]; [self.mapView removeFromSuperview]; // release crashes app self.mapView = nil; } @end ```

Original source