Chapter 25
Navigation

Modified

Disclosure

Some documentation from http://developer.apple.com/iphone/library/documentation/MapKit/Reference/MKAnnotation_Protocol/Reference/Reference.html

Example adapted from http://mithin.in/2009/06/22/using-iphone-sdk-mapkit-framework-a-tutorial

Downloads

MapApp iPhone example

Overview

Video

MapKit framework provides the display of a Google Map given a latitude and longitude.

MKMapView class displays a map, and is added to a view in IB by:

  1. In Xcode, create a View-based Application and name MapApp.
  2. Select Frameworks | Add | Existing frameworks | MapKit.framework
  3. Build.
  4. Open the MapAppViewController.
  5. In IB, open the Library, select Data Views | Map View
  6. Drag to the View.

To display a map centered at New Albany, Indiana requires an IBOutlet to the MKMapView object:

IBOutlet MKMapView *mapView;

and code to define the:

  1. latitude and longitude,
  2. degrees spanned by the map,
  3. and whether the map center is animated.
MKCoordinateRegion region;
MKCoordinateSpan span;

span.latitudeDelta=0.2;
span.longitudeDelta=0.2;

CLLocationCoordinate2D location = {latitude: 38.481057, longitude: -86.032563};

region.span=span;
region.center=location;

[mapView setRegion:region animated:TRUE];
[mapView regionThatFits:region];
 

MapAppViewController

More interesting is to have Google perform geocoding, looking up the longitude and latitude of a location.

A text field and button are added to the UI.

GoogleMaps response to: http://maps.google.com/maps/geo?q=47150&output=csv

200,5,38.2956537,-85.8383857

where 200 indicates success.

User input is inserted into the URL by:

NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv"",
                                  [addressField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

The latitude and longitude are then extracted from the returned text:

NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
NSArray *listItems = [locationString componentsSeparatedByString:@","];

aLatitude = [[listItems objectAtIndex:2] doubleValue];
aLongitude = [[listItems objectAtIndex:3] doubleValue];

Note that MKMapViewDelegate protocol provides optional callbacks, none of which are used here.

MapAppViewController.h

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface FirstMapAppViewController : UIViewController <MKMapViewDelegate> {
	IBOutlet UITextField *addressField;
	IBOutlet UIButton *goButton;
	IBOutlet MKMapView *mapView;
}
- (IBAction) showAddress;
@end
MapAppViewController.m
#import "MapAppViewController.h"
@implementation MapAppViewController

- (IBAction) showAddress {
	MKCoordinateRegion region;
	MKCoordinateSpan span;

	span.latitudeDelta=0.2;
	span.longitudeDelta=0.2;
        NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv",
                                          [addressField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
	NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
	NSArray *listItems = [locationString componentsSeparatedByString:@","];
	
	double aLatitude = 0.0;
	double aLongitude = 0.0;
	
	if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
		aLatitude = [[listItems objectAtIndex:2] doubleValue];
		aLongitude = [[listItems objectAtIndex:3] doubleValue];
	}
	else {
		[addressField setText: @"Address Location error"];
		return;
	}
	
	[addressField resignFirstResponder];

	CLLocationCoordinate2D location = {latitude: aLatitude, longitude:aLongitude};

	region.span=span;
	region.center=location;

	[mapView setRegion:region animated:TRUE];
	[mapView regionThatFits:region];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
- (void)viewDidUnload {}
- (void)dealloc {
    [super dealloc];
}

@end

Pins and Annotation

Map points can be marked by a notated pin. In this example, we place a pin at the center of the map and annotate the position with the latitude/longitude received by geocoding.

The following defines a MKMapView pin and annotation information. It is called to define the pin when the MKMapView object is displayed.

- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{

    MKPinAnnotationView *aView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                                                                     reuseIdentifier:@"currentloc"];
   aView.pinColor = MKPinAnnotationColorGreen;
   aView.animatesDrop=TRUE;
   aView.canShowCallout = YES;
   aView.calloutOffset = CGPointMake(-5, 5);
   return aView;
}

MKMapView objects receive the annotation information through a message that includes the object adopting the MKAnnotation protocol, here AddressAnnotation is the adopting class.

Adopting the MKAnnotation protocol requires one to define a coordinate property which is used for the annotation location.

@interface AddressAnnotation : NSObject <MKAnnotation> {

   CLLocationCoordinate2D coordinate;

   NSString *mTitle;
   NSString *mSubTitle;
}

For our example, the following constructs an object adopting the MKAnnotation protocol and sends as part of the message to the MKMapView object:

addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:location];
[mapView addAnnotation: addAnnotation];

MKAnnotation protocol is used to provide annotation-related information to a map view.

To use this protocol, adopt it in any custom objects that store or represent annotation data. Each object then serves as the source of information about a single map annotation and provides critical information, such as the annotation’s location on the map. Annotation objects do not provide the visual representation of the annotation but typically coordinate (in conjunction with the map view’s delegate) the creation of an appropriate MKAnnotationView object to handle the display. An object that adopts this protocol must implement the coordinate property. The other methods of this protocol are optional.

MapAppViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface AddressAnnotation : NSObject <MKAnnotation> {
	CLLocationCoordinate2D coordinate;
	
	NSString *mTitle;
	NSString *mSubTitle;
}

@end

@interface MapAppViewController : UIViewController<MKMapViewDelegate> {
	IBOutlet UITextField *addressField;
	IBOutlet UIButton *goButton;
	IBOutlet MKMapView *mapView;
	
	AddressAnnotation *addAnnotation;
}

- (IBAction) showAddress;
@end
MapAppViewController.m
#import "MapAppViewController.h"

@implementation AddressAnnotation

@synthesize coordinate;

- (NSString *)subtitle{
	return [[NSString alloc]initWithFormat: @"%f/%f", coordinate.latitude,coordinate.longitude];
}
- (NSString *)title{ return @"Latitude/Longitude"; }

-(id)initWithCoordinate:(CLLocationCoordinate2D) c{
	coordinate=c;
	return self;
}
@end

@implementation MapAppViewController
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
           MKPinAnnotationView *aView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                                                                            reuseIdentifier:@"currentloc"];
   aView.pinColor = MKPinAnnotationColorGreen;
   aView.animatesDrop=TRUE;
   aView.canShowCallout = YES;
   aView.calloutOffset = CGPointMake(-5, 5);
   return aView;
}
- (IBAction) showAddress {
	MKCoordinateRegion region;
	MKCoordinateSpan span;
	span.latitudeDelta=0.1;
	span.longitudeDelta=0.1;
	
	NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", 
				  [addressField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
	NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
	NSLog(@"addressLocation %@", locationString);
	NSArray *listItems = [locationString componentsSeparatedByString:@","];
	
	double latitude = 0.0;
	double longitude = 0.0;
	
	if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
		latitude = [[listItems objectAtIndex:2] doubleValue];
		longitude = [[listItems objectAtIndex:3] doubleValue];
	}
	else {
		[addressField setText: @"Address Location error"];
		return;
	}	
	[addressField resignFirstResponder];

	CLLocationCoordinate2D location;
	location.latitude = latitude;
	location.longitude = longitude;

	region.span=span;
	region.center=location;
	
	if(addAnnotation != nil) {
		[mapView removeAnnotation:addAnnotation];
		[addAnnotation release];
		addAnnotation = nil;
	}
addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:location];
[mapView addAnnotation: addAnnotation];
	[mapView setRegion:region animated:TRUE];
	[mapView regionThatFits:region];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
- (void)viewDidUnload {}
- (void)dealloc {
    [super dealloc];
}
@end