Make Better Things



I like to make better things.

Add Maps in iPhone apps (MapKit)

The MapKit framework allows you to integrate a quite fully-featured map in your applications. In this way, your user won’t need to quit your app to check the geographical information in the Maps app.

Let’s give a look at the MapKit and have some fun with the maps. Using Interface Builder you can embed a map in your app in few seconds. It is enough you just add a MKMapView object wherever you need and that’s it. But let’s try together.

Create a new View-based project and name it “Mapper”. The first thing we need to do is to add the MapKit framework. Go to Project -> Edit Active Target and choose the General tab. There, add the MapKit.framework to the Linked Libraries pressing the “+” button. Now, double click the MapperViewController.xib file. Then, drag a Map View object from the Library and drop it onto the View. Save and quit IB. Now, go back to Xcode and select Build and Run from the Build menu.

Map in Inspector

Map in Inspector

Do you like it? You got a fully featured map. You can zoom in and out and you can pan left and right and up and down. Now, re-open the MapperViewController.xib file and select the MapView you previously added to it. Open the Inspector and go to the Attributes pane.

As you can see, you can change different map attributes. For example, you can change the map type to Map, Satellite and Hybrid. You can allow the zooming and the scrolling and you can also activate the current user location. You can change these features in IB, but you can also change them programmatically. To do so, we need to create an outlet in your class and connect it to the map. Open MapperViewController.h and import the MapKit header. Add this line

#import <MapKit/MapKit.h>

Then, add the following outlet:

IBOutlet MKMapView *mapView;

Create also a property:

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

Remember to synthesize the property and don’t forget to release it in the dealloc method. Now, open MapperViewController.xib and connect the File’s Owner outlet (mapView) with the map object you previously added (see Figure below).

Map connection

Map connection

Now, you can go back to the code (after saving in IB) and add the following lines inside the viewDidiLoad method of the MapperViewController.m:

-(void) viewDidLoad
{
 [super viewDidLoad];
 [mapView setMapType:MKMapTypeStandard];
 [mapView setZoomEnabled:YES];
 [mapView setScrollEnabled:YES];
 }

Setting the Region

If you want to represent a specific portion of the map with a specific zoom level, you need to define a Map Region. This is a C structure composed by the Center and the Span. The Center is represented by the Longitude and the Latitude of the Region center, while the Span defines how much of the map is visible and is also the zoom level. A large Span corresponds to a low zoom level, while a small Span corresponds to a high zoom level.

Let’s define the Region for our map. At the end of the viewDidLoad method, add the following code:

MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = 41.902245099708516;
region.center.longitude = 12.457906007766724;
region.span.longitudeDelta = 0.01f;
region.span.latitudeDelta = 0.01f;
[mapView setRegion:region animated:YES];

The region is defined around the San Peter Cathedral in Rome. Now, if you want to pan or zoom programmatically, you just need to change programmatically the Region. If you change the Span, you zoom. If you change the Center, you pan. Simple, no?

User Location

This is very simple too. If you want to show the user location, you just set the flag in IB, as previously discussed, or you just add the following line of code:

[mapView setShowsUserLocation:YES];

Converting coordinates

It is often useful to convert the geographical coordinate to the view coordinates and vice versa. You can do this, using these methods:

– convertCoordinate:toPointToView:
– convertPoint:toCoordinateFromView:
– convertRegion:toRectToView:
- convertRect:toRegionFromView:

Annotations

A map without any useful information on it is most of the time useless. The magic happens when the map helps you to find some service or some friend or when it shows any interesting information to your app. If you want to add information to the map you need to add annotations.
In the MapKit framework, an annotation is composed by two parts: an annotation object and an annotation view. The Annotation Object contains information related to the geographical coordinates, while the Annotation View represents the view associated to the annotation object that will be displayed on request.
An annotation object is any object that conforms to the MKAnnotation protocol. Let’s add an annotation object to our map. We need to define a new class. Right-click the Classes Group and select Add->New Files.

Select an Objective-C class as Subclass of NSObject and name it MyAnnotation. Open the MyAnnotation.h and add the bold code:

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

@interface MyAnnotation : NSObject
{
     CLLocationCoordinate2D coordinate;
     NSString *title;
    NSString *subtitle;
}
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@end

Now, switch to the implementation file and add:

#import "MyAnnotation.h"

@implementation MyAnnotation

@synthesize coordinate, title, subtitle;

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

We have now our annotation class with the coordinates, a title and a subtitle for the Callout view (see below). Let’s instantiate an object and add it to the map.
Still in the viewDidLoad method add the following lines of code:

MyAnnotation *ann = [[MyAnnotation alloc] init];
ann.title = @"Rome";
ann.subtitle = @"San Peter";
ann.coordinate = region.center;
[mapView addAnnotation:ann];

Here, we created an ann object and set its title, subtitle and coordinates (for simplicity, I am setting the annotation coordinates to the map center, but you can put there any coordinates you like). You can create an array of annotations and then add it to the map.

Finally, we need to set our MapperViewController class as the delegate of the mapView. So, just add this line of code at the end of the viewDidLoad:

[mapView setDelegate:self];

and add the protocol name in the interface of the class:

@interface MapperViewController : UIViewController

The last step is to implement a delegate method that manages the annotations during the panning and zooming. The approach is very similar to the UITableView. We create a static identifier and we try to reuse the annotation view with the same identifier, as we do with the cell of the tableview. If we are not able to dequeue an annotation view, we need to allocate one. Then, we choose the pin color and allow a callout view. Then, we animate the drop of the pin.
Add the following code to the MapperViewController.m file:

- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id )annotation
{
    MKPinAnnotationView *pinView = nil;
    if(annotation != mapView.userLocation)
    {
         static NSString *defaultPinID = @"com.invasivecode.pin";
         pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
         if ( pinView == nil )
            pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
        pinView.pinColor = MKPinAnnotationColorPurple;
        pinView.canShowCallout = YES;
        pinView.animatesDrop = YES;
    }
    else
        [mapView.userLocation setTitle:@"I am here"];
    return pinView;
}

That’s it. Below, you can see the result.

MapKit on iPhone

MapKit on iPhone

Category: iPhone

Tagged:

6 Responses

  1. Thanks for such informative article and hopes that you will keep the good work on…………

  2. mahi says:

    i have get latitude and longitude of current location but i don’t get blue dot and circle on map and i have get current latitude and longitude and pass in web service URL and according to current latitude and longitude i have drooped annotation pin on map in 2.0 miles but i don’t get blue dot and circle of current location on map plz help me

    • Saurabh says:

      Implement the delegate method:

      - (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation: (id)annotation

      and return nil if if (annotation == mv.userLocation). This tell the MKMapView to use whatever standard annotation it deems appropriate.

  3. Alson says:

    hi !
    how do i add more annotations to the current MKMapview!
    been searching on google but the other codes are too difficult to understand!

  4. Saurabh says:

    @Alson –

    You can loop the code where we are addding anotations i.e. –


    for(int i = 0; i < [Arr count]; i++)
    {

    MyAnnotation *ann = [[MyAnnotation alloc] init];
    ann.title = @"Rome";
    ann.subtitle = @"San Peter";
    ann.coordinate = region.center;
    [mapView addAnnotation:ann];

    }

  5. Harshit Gupta says:

    Thanks a lot, buddy…
    the tutorial is simply the best.

Leave a Reply