Creating a Map Based Application on the iPhone Part 1

Everybody seems to want a map based mobile application these days. It’s like the late 90s all over again but with mobile devices instead of Netscape. So to lighten my work load a bit I’m going to show you how to go about making your own, and a few tricks I’ve learnt along the way.

I’m not going to go into detail on how to hook up the IBOutlets or add frameworks to your project, so if you don’t know how to do that this tutorial might be too advanced. Apple have some great sample code for working with the MapKit framework, so as well as doing this tut I suggest you download Apple’s example from here.

I’ve included the project files with this tut at the end, so feel free to download and have a play around. In this tut we’re going to get a map view displaying an annotation (pin) on the map and a map callout popping up with some text on user tap.
The first thing we want to do is create a new view based application in the XCode templates. I’ve called mine MapKitTutorial. We need to add the MapKit framework to our project, this is a very important step and very easy to overlook (trust me, I’ve spent a fair share of hours banging my head against the table). So in your Build Phases under Link Binary With Libraries add MapKit.framework. Now open the MapKitTutorialViewController.h file, the view controller that is visible when the app loads, and get it looking something like this:

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

@interface MapKitTutorialViewController : UIViewController <MKMapViewDelegate> {
    MKMapView *mapView;
    NSMutableArray *mapAnnotations;
}

@property (nonatomic, retain) IBOutlet MKMapView *mapView;

@end


we’ve imported the MapKit framework as well as the UIKit framework, and we set the class to conform to the MKMapViewDelegate. We then created an instance of a MapView to hook up in Interface Builder.
Now open MapKitTutorialViewController.xib (or whatever the xib is that is being loaded into your app) in Interface Builder and drag on a MapView component from the component list. In Interface Builder create a new Referencing Outlet on the MapView and hook it up to the MapView IBOutlet and also set the delegate to the File’s Owner.
If you run this app now in the Simulator it should work, you’ll see a world map. This is the minimum you have to do to get a map running in your app, it doesn’t do much, but hey you’ve got a map!
So let’s add an annotation (map pin) and annotation “callout” for when the user taps. Get your .m file looking like this:

#import "MapKitTutorialViewController.h"
#import <MapKit/MapKit.h>
#import "SydneyAnnotation.h"

@implementation MapKitTutorialViewController
@synthesize mapView;

- (void)dealloc
{
    //dealloc
    [super dealloc];
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.mapView.mapType = MKMapTypeStandard;
    
    // set the map to show Sydney
    MKCoordinateRegion sydneyRegion;
    sydneyRegion.center.latitude = -33.870883;
    sydneyRegion.center.longitude = 151.2025;
    sydneyRegion.span.latitudeDelta = 0.05;
    sydneyRegion.span.longitudeDelta = 0.05;
    [self.mapView setRegion:sydneyRegion animated:YES];
    // annotation for Sydney
    SydneyAnnotation *sydneyAnnotation = [[SydneyAnnotation alloc] init];
    [self.mapView addAnnotation:sydneyAnnotation];
    [sydneyAnnotation release];
}

- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
    // try to dequeue an existing pin view first
    static NSString* SydneyAnnotationIdentifier = @"SydneyAnnotationIdentifier";
    MKPinAnnotationView* pinView = (MKPinAnnotationView *)
    [mapView dequeueReusableAnnotationViewWithIdentifier:SydneyAnnotationIdentifier];
    if (!pinView)
    {
        // if an existing pin view was not available, create one
        MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
                                               initWithAnnotation:annotation reuseIdentifier:SydneyAnnotationIdentifier] autorelease];
        customPinView.pinColor = MKPinAnnotationColorPurple;
        customPinView.animatesDrop = YES;
        customPinView.canShowCallout = YES;
        /*
        UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        [rightButton addTarget:self
                        action:@selector(showDetails:)
              forControlEvents:UIControlEventTouchUpInside];
        customPinView.rightCalloutAccessoryView = rightButton;
        */
        return customPinView;
    }
    else
    {
        pinView.annotation = annotation;
    }
    return pinView;    
}

@end

So when the view loads we set the region to Sydney and then we’re creating an annotation and adding it to the map in the center of Sydney. This is a custom class that conforms to the “MKAnnotation delegate”, we’ll be creating this next. Then we override the ViewForAnnotation method of the MapKitDelegate and add the code to create our annotation, or just reuse the current annotation if it already exists.

Next lets get the custom annotation class set up. Create a new objective C class that extends NSObject, I’ve called mine SydneyAnnotation, and then get the .h file looking like this:

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

@interface SydneyAnnotation : NSObject <MKAnnotation>{
    
}

@end

So that was pretty easy, all we’re doing is conforming the class to MKAnnotation. and the .m file looks like this:

#import "SydneyAnnotation.h"


@implementation SydneyAnnotation

- (CLLocationCoordinate2D)coordinate;
{
    CLLocationCoordinate2D theCoordinate;
    theCoordinate.latitude = -33.870883;
    theCoordinate.longitude = 151.2025;
    return theCoordinate; 
    
}

// required if you set the MKPinAnnotationView's "canShowCallout" property to YES
- (NSString *)title
{
    return @"Sydney Yo";
}

// optional
- (NSString *)subtitle
{
    return @"Koala Bears!";
}

- (void)dealloc
{
    [super dealloc];
}
@end

So we’re setting the longs and lats of the annotation and a title and a subtitle. An annotation is just a generic object that conforms to MKAnnotation, which gives it some extra properties, coordinate, title and subtitle. So when MapKit cycles through and looks at this annotation object it looks for the coordinate value to place it’s location and it looks for a title and subtitle for the annotation’s callout when it is tapped. Of course this can all be added dynamically and also a button that drills down to a detail view, and we’ll be doing that in the coming tuts.

Download the project files here

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *