Programmatically open Maps app in iOS 6
IosObjective CIos6MapkitIos Problem Overview
Previous to iOS 6, opening a URL like this would open the (Google) Maps app:
NSURL *url = [NSURL URLWithString:@"http://maps.google.com/?q=New+York"];
[[UIApplication sharedApplication] openURL:url];
Now with the new Apple Maps implementation, this just opens Mobile Safari to Google Maps. How can I accomplish the same behavior with iOS 6? How do I programmatically open the Maps app and have it point to a specific location/address/search/whatever?
Ios Solutions
Solution 1 - Ios
Here's the official Apple way:
// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)])
{
// Create an MKMapItem to pass to the Maps app
CLLocationCoordinate2D coordinate =
CLLocationCoordinate2DMake(16.775, -3.009);
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate
addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:@"My Place"];
// Pass the map item to the Maps app
[mapItem openInMapsWithLaunchOptions:nil];
}
If you want to get driving or walking instructions to the location, you can include a mapItemForCurrentLocation
with the MKMapItem
in the array in +openMapsWithItems:launchOptions:
, and set the launch options appropriately.
// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)])
{
// Create an MKMapItem to pass to the Maps app
CLLocationCoordinate2D coordinate =
CLLocationCoordinate2DMake(16.775, -3.009);
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate
addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:@"My Place"];
// Set the directions mode to "Walking"
// Can use MKLaunchOptionsDirectionsModeDriving instead
NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking};
// Get the "Current User Location" MKMapItem
MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
// Pass the current location and destination map items to the Maps app
// Set the direction mode in the launchOptions dictionary
[MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem]
launchOptions:launchOptions];
}
You can preserve your original iOS 5 and lower code in an else
statement after that if
. Note that if you reverse the order of items in the openMapsWithItems:
array, you'll get directions from the coordinate to your current location. You could probably use it to get directions between any two locations by passing a constructed MKMapItem
instead of the current location map item. I haven't tried that.
Finally, if you have an address (as a string) that you want directions to, use the geocoder to create an MKPlacemark
, by way of CLPlacemark
.
// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)])
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:@"Piccadilly Circus, London, UK"
completionHandler:^(NSArray *placemarks, NSError *error) {
// Convert the CLPlacemark to an MKPlacemark
// Note: There's no error checking for a failed geocode
CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
MKPlacemark *placemark = [[MKPlacemark alloc]
initWithCoordinate:geocodedPlacemark.location.coordinate
addressDictionary:geocodedPlacemark.addressDictionary];
// Create a map item for the geocoded address to pass to Maps app
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:geocodedPlacemark.name];
// Set the directions mode to "Driving"
// Can use MKLaunchOptionsDirectionsModeWalking instead
NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
// Get the "Current User Location" MKMapItem
MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
// Pass the current location and destination map items to the Maps app
// Set the direction mode in the launchOptions dictionary
[MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem] launchOptions:launchOptions];
}];
}
Solution 2 - Ios
Found the answer to my own question. Apple documents its maps URL format here. It looks like you can essentially replace maps.google.com
with maps.apple.com
.
Update: It turns out that the same is true in MobileSafari on iOS 6; tapping a link to http://maps.apple.com/?q=...
opens the Maps app with that search, the same way http://maps.google.com/?q=...
did on previous versions. This works and is documented in the page linked above.
UPDATE: This answers my question relating to the URL format. But nevan king's answer here (see below) is an excellent summary of the actual Maps API.
Solution 3 - Ios
The best way to do it is to call new iOS 6 method on MKMapItem
openInMapsWithLaunchOptions:launchOptions
Example:
CLLocationCoordinate2D endingCoord = CLLocationCoordinate2DMake(40.446947, -102.047607);
MKPlacemark *endLocation = [[MKPlacemark alloc] initWithCoordinate:endingCoord addressDictionary:nil];
MKMapItem *endingItem = [[MKMapItem alloc] initWithPlacemark:endLocation];
NSMutableDictionary *launchOptions = [[NSMutableDictionary alloc] init];
[launchOptions setObject:MKLaunchOptionsDirectionsModeDriving forKey:MKLaunchOptionsDirectionsModeKey];
[endingItem openInMapsWithLaunchOptions:launchOptions];
This will start the navigation for driving from the current location.
Solution 4 - Ios
I see you found the maps.apple.com url "scheme". It's a good choice because it will automatically redirect older devices to maps.google.com. But for iOS 6 there is a new class you might want to take advantage of: MKMapItem.
Two methods that are of interest to you:
- -openInMapsWithLaunchOptions: - call it on an MKMapItem instance to open it in Maps.app
- +openMapsWithItems:launchOptions: - call it on MKMapItem class to open an array of MKMapItem instances.
Solution 5 - Ios
Here is a class using nevan king's solution completed in Swift:
class func openMapWithCoordinates(theLon:String, theLat:String){
var coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(theLon), CLLocationDegrees(theLat))
var placemark:MKPlacemark = MKPlacemark(coordinate: coordinate, addressDictionary:nil)
var mapItem:MKMapItem = MKMapItem(placemark: placemark)
mapItem.name = "Target location"
let launchOptions:NSDictionary = NSDictionary(object: MKLaunchOptionsDirectionsModeDriving, forKey: MKLaunchOptionsDirectionsModeKey)
var currentLocationMapItem:MKMapItem = MKMapItem.mapItemForCurrentLocation()
MKMapItem.openMapsWithItems([currentLocationMapItem, mapItem], launchOptions: launchOptions)
}
Solution 6 - Ios
I found it annoying that using the http://maps.apple.com?q=... link setup opens the safari browser first at older devices.
So for an iOS 5 device opening up your app with a reference to maps.apple.com the steps look like:
- you click something in the app and it refers to the maps.apple.com url
- safari opens up the link
- the maps.apple.com server redirects to the maps.google.com url
- the maps.google.com url gets interpreted and opens the google Maps app.
I think that the (very obvious and confusing) steps 2 and 3 are annoying to users. Therefore i check the os version and either run maps.google.com or maps.apple.com on the device (for resp. ios 5 or ios 6 OS versions).
Solution 7 - Ios
My research on this issue lead me to the following conclusions:
- If you use maps.google.com then it will open the map in safari for every ios.
- If you use maps.apple.com then it will open the map in map application of ios 6 and also work greate with ios 5 and in ios 5 it open the map as normal in safari.
Solution 8 - Ios
If you want to open Google Maps instead (or offer as a secondary option), you can use the comgooglemaps://
and comgooglemaps-x-callback://
URL schemes documented here.
Solution 9 - Ios
Before launching url, remove any special character from the url and replace spaces by +. This will save you some headaches:
NSString *mapURLStr = [NSString stringWithFormat: @"http://maps.apple.com/?q=%@",@"Limmattalstrasse 170, 8049 Zürich"];
mapURLStr = [mapURLStr stringByReplacingOccurrencesOfString:@" " withString:@"+"];
NSURL *url = [NSURL URLWithString:[mapURLStr stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
if ([[UIApplication sharedApplication] canOpenURL:url]){
[[UIApplication sharedApplication] openURL:url];
}
Solution 10 - Ios
NSString *address = [NSString stringWithFormat:@"%@ %@ %@ %@"
,[dataDictionary objectForKey:@"practice_address"]
,[dataDictionary objectForKey:@"practice_city"]
,[dataDictionary objectForKey:@"practice_state"]
,[dataDictionary objectForKey:@"practice_zipcode"]];
NSString *mapAddress = [@"http://maps.apple.com/?q=" stringByAppendingString:[address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"Map Address %@",mapAddress);
[objSpineCustomProtocol setUserDefaults:mapAddress :@"webSiteToLoad"];
[self performSegueWithIdentifier: @"provider_to_web_loader_segue" sender: self];
//VKJ
Solution 11 - Ios
Not using maps, just programmatically using a UiButton action, this worked great for me.
// Button triggers the map to be presented.
@IBAction func toMapButton(sender: AnyObject) {
//Empty container for the value
var addressToLinkTo = ""
//Fill the container with an address
self.addressToLinkTo = "http://maps.apple.com/?q=111 Some place drive, Oak Ridge TN 37830"
self.addressToLinkTo = self.addressToLinkTo.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let url = NSURL(string: self.addressToLinkTo)
UIApplication.sharedApplication().openURL(url!)
}
You could spread some of this code out a bit. For example, I put the variable as a class level variable, had another function fill it, and then when pressed the button simply took what was in the variable and scrubbed it to be used in a URL.
Solution 12 - Ios
Updated to Swift 4 based on @PJeremyMalouf's answer:
private func navigateUsingAppleMaps(to coords:CLLocation, locationName: String? = nil) {
let placemark = MKPlacemark(coordinate: coords.coordinate, addressDictionary:nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = locationName
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
let currentLocationMapItem = MKMapItem.forCurrentLocation()
MKMapItem.openMaps(with: [currentLocationMapItem, mapItem], launchOptions: launchOptions)
}