Integrating Maps into iPhone iOS 6 Applications using MKMapItem
Previous | Table of Contents | Next |
An iOS 6 iPhone State Preservation and Restoration Tutorial | An Example iOS 6 iPhone MKMapItem Application |
Learn SwiftUI and take your iOS Development to the Next Level |
If there is one single fact about Apple that we can state with any degree of certainty, it is that the company is fanatical about retaining control of its own destiny. One glaring omission in this overriding corporate strategy has been the reliance on a competitor (in the form of Google) for mapping data in iOS. This dependency officially ends with iOS 6 through the introduction of Apple Maps.
Apple Maps officially replaces the Google-based map data of previous iOS releases with data provided primarily by a company named TomTom (but also including technology from other companies, including some acquired by Apple for this purpose). Headquartered in the Netherlands, TomTom specializes in mapping and GPS systems. Of particular significance, however, is that TomTom (unlike Google) does not make smartphones, nor does it develop an operating system that competes with iOS, making it a more acceptable partner for Apple.
Political issues aside, there are also technological advantages to the change. Of particular significance is the fact that Google maps were assembled from collections of static images. This led to fuzzy images when zooming in and out and a lack of precision when declaring map regions. The new maps in iOS 6 are dynamically rendered, vector-based images making them both scalable and more precise.
As part of the iOS 6 revamp of mapping, the SDK also introduces a new class in the form of MKMapItem, designed solely for the purpose of easing the integration of maps and turn-by-turn directions into iOS applications.
For more advanced mapping requirements, the iOS 6 SDK still includes the original classes of the MapKit framework, details of which will be covered in later chapters.
MKMapItem and MKPlacemark Classes
The purpose of the MKMapItem class is to make it easy for applications to launch maps without having to write significant amounts of code. MKMapItem works in conjunction with the MKPlacemark class, instances of which are passed to MKMapItem to define the locations that are to be displayed in the resulting map. A range of options are also provided with MKMapItem to configure both the appearance of maps and the nature of turn-by-turn directions that are to be displayed (i.e. whether directions are to be for driving or walking).
An Introduction to Forward and Reverse Geocoding
It is difficult to talk about mapping, in particular when dealing with the MKPlacemark class, without first venturing into the topic of geocoding. Geocoding can best be described as the process of converting a textual based geographical location (such as a street address) into geographical coordinates expressed in terms of longitude and latitude.
Within the context of iOS 6 development, geocoding may be performed by making use of the CLGeocoder class which is used to convert a text based address string into a CLLocation object containing the coordinates corresponding to the address. The following code, for example, converts the street address of the Empire State Building in New York to longitude and latitude coordinates:
[geocoder geocodeAddressString:@"350 5th Avenue New York, NY" completionHandler:^(NSArray *placemarks, NSError *error) { if (error) { NSLog(@"Geocode failed with error: %@", error); return; } if(placemarks && placemarks.count > 0) { CLPlacemark *placemark = placemarks[0]; CLLocation *location = placemark.location; CLLocationCoordinate2D coords = location.coordinate; NSLog(@"Latitude = %f, Longitude = %f", coords.latitude, coords.longitude); } } ];
The code simply calls the geocodeAddressString: method of a CLGeocoder instance, passing through a string object containing the street address and a completion handler to be called when the translation is complete. Passed as arguments to the handler are an array of CLPlacemark objects (one for each match for the address) together with an Error object which may be used to identify the reason for any failures.
For the purposes of this example the assumption is made that only one location matched the address string provided. The location information is then extracted from the CLPlacemark object at location 0 in the array and the coordinates displayed on the console.
The above code is an example of forward-geocoding in that coordinates are calculating based on a text address description. Reverse-geocoding, as the name suggests, involves the translation of geographical coordinates into a human readable address string. Consider, for example, the following code:
CLGeocoder *geocoder = [[CLGeocoder alloc] init]; CLLocation *newLocation = [[CLLocation alloc]initWithLatitude:40.74835 longitude:-73.984911]; [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) { if (error) { NSLog(@"Geocode failed with error: %@", error); return; } if (placemarks && placemarks.count > 0) { CLPlacemark *placemark = placemarks[0]; NSDictionary *addressDictionary = placemark.addressDictionary; NSString *address = [addressDictionary objectForKey:(NSString *)kABPersonAddressStreetKey]; NSString *city = [addressDictionary objectForKey:(NSString *)kABPersonAddressCityKey]; NSString *state = [addressDictionary objectForKey:(NSString *)kABPersonAddressStateKey]; NSString *zip = [addressDictionary objectForKey:(NSString *)kABPersonAddressZIPKey]; NSLog(@"%@ %@ %@ %@", address,city, state, zip); } }];
In this case, a CLLocation object is initialized with longitude and latitude coordinates and then passed through to the reverseGeocodeLocation: method of a CLGeocoder object. The method passes through to the completion handler an array of matching addresses in the form of CLPlacemark objects. Each object contains an NSDictionary object which, in turn, contains the address information for the matching location. Once again, the code assumes a single match is contained in the array and uses the dictionary keys to access and display the address, city, state, zip and country values. The address dictionary keys follow the standard defined in the Address Property section of the iOS SDK Address Book Person reference.
When executed, the above code results in output which reads:
338 5th Avenue New York, New York 10001, United States
It should be noted that the geocoding is not actually performed on the iPhone device, but rather on a server to which the device connects when a translation is required and the results subsequently returned when the translation is complete. As such, geocoding can only take place when the iPhone has an active internet connection.
Creating MKPlacemark Instances
Each location that is to be represented when a map is displayed using the MKMapItem class must be represented by an MKPlacemark object. When MKPlacemark objects are created, they must be initialized with the geographical coordinates of the location together with an NSDictionary object containing the address property information. Continuing the example for the Empire State Building in New York, an MKPlacemark object would be created as follows:
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(40.74835, -73.984911); NSDictionary *address = @{ (NSString *)kABPersonAddressStreetKey: @"350 5th Avenue", (NSString *)kABPersonAddressCityKey: @"New York", (NSString *)kABPersonAddressStateKey: @"NY", (NSString *)kABPersonAddressZIPKey: @"10118", (NSString *)kABPersonAddressCountryCodeKey: @"US" }; MKPlacemark *place = [[MKPlacemark alloc] initWithCoordinate:coords addressDictionary:address];
Whilst it is possible to initialize an MKPlacemark object passing through a nil value for the address dictionary, this will result in the map appearing, albeit with the correct location marked, but it will be tagged as “Unknown” instead of listing the address. The coordinates are, however, mandatory when creating an MKPlacemark object. In the event that the application knows the text address but not the coordinates of a location, geocoding will need to be used to obtain the coordinates prior to creating the MKPlacemark instance.
Working with MKMapItem
Given the tasks that it is able to perform, the MKMapItem class is actually extremely simple to use. In its simplest form, it can be initialized via a call to the initWithPlacemark: method, passing through a single MKPlacemark object as an argument, for example:
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:myplacemark];
Once initialized, the openInMapsWithLaunchOptions: method will open the map positioned at the designated location with an appropriate marker as illustrated in Figure 59-1:
[mapItem openInMapsWithLaunchOptions:nil];
Figure 59-1
Similarly, the map may be initialized to display the current location of the user’s device via a call to the mapItemForCurrentLocation: method:
MPMapItem *item = [MKMapItem mapItemForCurrentLocation];
Multiple locations may be tagged on the map by placing two or more MKMapItem objects in an array and then passing that array through to the openMapsWithItems: class method of the MKMapItem class. For example:
NSArray *mapItem = @[mapItem1, mapItem2, mapItem3]; [MKMapItem openMapsWithItems:mapItems launchOptions:nil];
MKMapItem Options and Enabling Turn-by-Turn Directions
In the example code fragments presented in the preceding sections, a nil value was passed through as the options argument to the MKMapItem methods. In actual fact, there are a number of configuration options that are available for use when opening a map. These values need to be set up within an NSDictionary object using a set of pre-defined keys and values:
- MKLaunchOptionsDirectionsModeKey – Controls whether turn-by-turn directions are to be provided with the map. In the event that only one placemarker is present, directions from the current location to the placemarker will be provided. The mode for the directions should be one of either MKLaunchOptionsDirectionsModeDriving or MKLaunchOptionsDirectionsModeWalking.
- MKLaunchOptionsMapTypeKey – Indicates whether the map should display satellite, hybrid or standard map images.
- MKLaunchOptionsMapCenterKey – Corresponds to a CLLocationCoordinate2D structure value containing the coordinates of the location on which the map is to be centered.
- MKLaunchOptionsMapSpanKey – An MKCoordinateSpan structure value designating the region that the map should display when launched.
- MKLaunchOptionsShowsTrafficKey – A Boolean value indicating whether or not traffic information should be superimposed over the map when it is launched.
The following code, for example, opens a map in satellite mode with traffic data displayed and includes turn-by-turn driving directions between two map items, the result of which is illustrated in Figure 59-2:
NSArray *mapItem = @[mapItem1, mapItem2]; NSDictionary *options = @{ MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsMapTypeKey: [NSNumber numberWithInteger:MKMapTypeSatellite], MKLaunchOptionsShowsTrafficKey:@YES }; [MKMapItem openMapsWithItems:mapItems launchOptions:options];
Figure 59-2
Adding Item Details to an MKMapItem
When a location is marked on a map, the address is displayed together with a blue arrow which, when selected, displays an information card for that location. Figure 59-3 shows the location marker for the Empire State Building alongside the information card displayed when that location marker is selected:
Figure 59-3
The MKMapItem class allows additional information to be added to a location through the name, phoneNumber and url properties. The following code, for example, adds these properties to the map item for the Empire State Building:
mapItem.name = @"Empire State Building"; mapItem.phoneNumber = @"+12127363100"; mapItem.url = [NSURL URLWithString:@"http://www.esbnyc.com/"]; [mapItem openInMapsWithLaunchOptions:options];
When the code is executed, the map place marker displays the location name instead of the address and the information card includes the phone number and URL:
Figure 59-4
Summary
iOS 6 replaces Google Maps with maps provided by TomTom. Unlike Google Maps, which were assembled from static images, the new Apple Maps are dynamically rendered resulting in clear and smooth zooming and more precise region selections. iOS 6 also introduced the MKMapItem class, the purpose of which is to make it easy for iOS application developers to launch maps and provide turn-by-turn directions with the minimum amount of code.
Within this chapter, the basics of geocoding and the MKPlacemarker and MKMapItem classes have been covered. In the next chapter, entitled An Example iOS 6 iPhone MKMapItem Application will work through the creation of an example application that utilizes the knowledge covered in this chapter.
Learn SwiftUI and take your iOS Development to the Next Level |
Previous | Table of Contents | Next |
An iOS 6 iPhone State Preservation and Restoration Tutorial | An Example iOS 6 iPhone MKMapItem Application |