MapKit

Location, Location, Location is all the rage at the moment. Where did someone last update their social network status ? Where is the nearest restaurant ? Where do I need to be in 5 mins ? the possibilities for location services are (nearly) endless. But what is a location ? well put simple it is just a co-ordinate value (longitude and latitude), and in the case of iPhone, it is in reference to the earth. But lets be honest, displaying -122.03, 37.33 in your sexy new iPhone application is not very exciting now is it ? That is were MKMapView comes in.

MKMapView allows you to display a Map on your iPhone’s screen. MKMapView is just a subclass of UIView so you can treat it like one. You can ether use it as a subview (such as on a profile page), or make it go full screen (using it with its own view controller) such as when you are viewing a map to get directions.

So like UIView, to create a MKMapView you need to initialise one with a frame:

MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0,0,320,480)];

Unlike UIView, you will also want to become the delegate of MKMapView.

mapView.delegate = self;

The delegate methods for MKMapView include ones that inform you about when the map starts and finishes to load, in addition to if it fails to load at all. (These delegate methods are very similar to UIWebView if you have used that).

- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView;
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error;

In addition to the loading of the Map, the delegate also provides call backs for when the map’s region changes, it also provides callbacks regarding annotation information (more on that later).

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;

After you have created a MKMapView, you will want to show a specific region on the map. The method to do this is:

- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;

This introduces a new MapKit data type MKCoordinateRegion. This is the centre point (that you want to show) expressed as a co-ordinate, and then the distance (span) around this co-ordinate that you want to be shown. In essence the span is the zoom level. The bigger the span, the bigger the area that is displayed on the map.

To make a new region you will need to use:

MKCoordinateRegionMake(CLLocationCoordinate2D centerCoordinate, MKCoordinateSpan span);

And thus you will probably have some code that looks like this:

CLLocationCoordinate2D coordinate;
coordinate.latitude = -122.03;
coordinate.longitude = 37.33;

MKCoordinateSpan span = MKCoordinateSpanMake(0.003, 0.003);

[mapView setRegion:MKCoordinateRegionMake(coordinate, MKCoordinateSpanMake(0.003, 0.003)) animated:YES];

After you have focused in on a specific region of the map, you will more than likely want to annotate a given point. To do this you need to create an object that conforms to the (informal) MKAnnotation protocol, and in particular implements the CLLocationCoordinate2D coordinate property.

e.g.

@interface MCSMMapAnnotation : NSObject {

}

@property (nonatomic) CLLocationCoordinate2D coordinate;

@end

Then you just need to create an instance of this model object, and then add it to the map as an annotation:

MCSMMapAnnotation *annotation = [[MCSMMapAnnotation alloc] init];

CLLocationCoordinate2D annotationCoordinate;
coordinate.latitude = -122.03;
coordinate.longitude = 37.33;
annotation.coordinate = annotationCoordinate;
[self.mapView addAnnotation:annotation];

And that is it, nice and simple.

One more thing …

Before I mentioned that there are delegate methods regarding the annotations on the Map View. One that is particular useful is:

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews;

Why is it useful ? Well if you didn’t notice, doing the above means that the annotation(s) just appear on the screen and they do not drop on. So in this delegate method, you can move the annotations off screen and then put them back to where MapKit said they should be.

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews {

    // The final (correct) position of the annotation
    CGRect finalFrame;

    // The position we will drop the annotation from
    CGRect offScreenFrame;      

    for(UIView *annotationView in annotationViews) {
        // MapKit has worked out the annotations final position so store it
        finalFrame = annotationView.frame;

        // We just want to move the annotation, so it is just above the top of the visible screen

        offScreenFrame = CGRectMake(finalFrame.origin.x,(finalFrame.size.height *-1) , finalFrame.size.width, finalFrame.size.height);

        // Set and therefore move the annotation to the off screen position
        annotationView.frame = offScreenFrame;

        // Set up an animation block to animate the drop
        [UIView beginAnimations:@"AnimateAnnotation" context:NULL];
        [UIView setAnimationDuration:1.0];

        // Set the final frame, to be the frame that map kit originally calculated
        annotationView.frame = finalFrame;
        [UIView commitAnimations];
    }       
}

This is just a brief overview of what you can do with MapKit. The most obvious thing you can do, is to add more than one annotation to the map, and the above animation trick is designed to work with this. One feature you may want to do if your application heavily relies upon maps, is to create your own custom pins (that don’t even need to look like pins), to point out specific points on the map.