How to Create Annotations to Customize Your iOS App Map - dummies

How to Create Annotations to Customize Your iOS App Map

By Neal Goldstein, Dave Wilson

Any object in your iOS app that conforms to the MKAnnotation protocol is an Annotation object; typically, Annotation objects are existing classes in your application’s model. The job of an Annotation object is to know its location (coordinates) on the map along with the text to be displayed in the callout.

The MKAnnotation protocol requires a class that adopts that protocol to implement the coordinate property. It can also optionally implement title and subtitle properties. In that case, that text will be displayed in the annotation callout when the user taps the annotation.

Annotations are required by the protocol to have the properties bolded in the following code:

@property (nonatomic, readwrite)       CLLocationCoordinate2D coordinate;@property
(nonatomic, readwrite, copy) NSString *title;@property (nonatomic, readwrite, copy)
NSString *subtitle;

That’s it. You already have the properties in place and initialized.

You want your points of interest to be annotations as well.

You’re going to need to go back to the object to create the annotations, but first you have to add an Annotation class to the Model Classes group by following these steps:

  1. In the Project navigator, select the Model Classes group, and then either right-click the selection and choose New File from the menu that appears or choose File→New→File from the main menu (or press ⌘+N) to open the New File dialog.

  2. In the left column of the dialog, select Cocoa Touch under the iOS heading, select the Objective-C Class template in the top-right pane, and then click Next.

    You’ll see a dialog that will enable you to choose the options for your file.

  3. Enter Annotation in the Class field.

  4. Choose or enter NSObject in the Subclass Of field and then click Next.

    Note that the Target for iPad and With XIB for User Interface check boxes are dimmed because they are not relevant here — Events is derived from NSObject, and not any type of view controller.

  5. In the Save sheet that appears, click Create.

Next, you need to add the code necessary for an annotation.

Add the code in bold to Annotation.h.

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface Annotation: NSObject <MKAnnotation>
@property (nonatomic, readwrite)
CLLocationCoordinate2D coordinate;
@property (nonatomic, readwrite, copy) NSString *title;
@property (nonatomic, readwrite, copy) NSString *subtitle;

You’re using a generic Annotation class to display the points of interest. As you build out the app, you could also include more information about the points of interest and other information, and create a PointofInterest class.

Then you could make it a subclass Annotation. In an annotation, you can also have a right and left Callout Accessory view, which display on the right and left side of the standard callout bubble, respectively.

The Callout view is typically used to link to more detailed information about the annotation. Also, you could link to something such as the EventController to display information about a PointofInterest. Just food for thought.

#import "Trip.h"
#import "Destination.h"
#import "Events.h"
#import "Annotation.h"
@interface Trip ()
@property (strong, nonatomic) NSDictionary
@property (strong, nonatomic) Destination *destination;
@property (strong, nonatomic) Events *events;
@property (strong, nonatomic) NSMutableArray *pois;

Now you can add the bolded code in Listing 17-11 to initWith DestinationIndex: in Trip.m. This will turn the point-of-interest data in the plist (the POIs) into annotations (okay, just one) and add the destination as an annotation to boot.

- (id)initWithDestinationIndex:(int)destinationIndex {
self = [super init];
if (self)) {
NSString *filePath = [[NSBundle mainBundle]
pathForResource:@"Destinations" ofType:@"plist"];
NSDictionary *destinations =
[NSDictionary dictionaryWithContentsOfFile:filePath];
NSArray *destinationsArray =
[destinations objectForKey:@"DestinationData"];
_destinationData =
[destinationsArray objectAtIndex:destinationIndex];
_destination = [[Destination alloc]
events = [[Events alloc]
NSArray *poiData = self.destinationData[@"POIs"];
_pois = [[NSMutableArray alloc]
initWithCapacity:[ poiData count]+1];
[_pois addObject: self.destination];
for (NSDictionary *aPOI in poiData) {
Annotation *annotation = [[Annotation alloc] init];
CLLocationCoordinate2D coordinate;
coordinate.latitude =
[aPOI[@"Latitude"] doubleValue];
coordinate.longitude =
[aPOI[@"Longitude"] doubleValue];
annotation.coordinate = coordinate;
annotation.title = aPOI[@"Title"];
annotation.subtitle = aPOI[@"Subtitle"];
[self.pois addObject:annotation];
return self;

As you can see, you’re creating an Annotation for each point of interest in the poiData array and adding it to a array you create — an array that will hold all the annotations you want to display on the map.

If you look closely, you can see that Destination is being added to the pois array as well. That way, it, too, will display on the map.

You have to add some new methods to the interface so that Trip can return the annotations (and a map title). You also need to update the Tripinterface. To do that, add the bolded code to Trip.h.

@interface Trip : NSObject
- (UIImage *) destinationImage;
- (NSString *) destinationName;
- (CLLocationCoordinate2D) destinationCoordinate;
- (id)initWithDestinationIndex:(int)destinationIndex;
- (NSString *)weather;
- (NSUInteger)numberOfEvents;
- (NSString *)getEvent:(NSUInteger)index;
- (NSArray *)createAnnotations;
- (NSString *)mapTitle;

Now you get to add all the Trip methods that will be used by the MapController. Start by adding the createAnnotations method to Trip.m.

- (NSArray *)createAnnotations {
return self.pois;

Even though pois is a mutable array, you return it as a basic array because that’s all that is needed. MapController won’t be adding any annotations to it.

You also need to add a method to return the map title. Add the maptitle method to Trip.m.

- (NSString *)mapTitle {
return self.destination.destinationName;