Displaying Maps and Annotations IPHONE APPS

Introduced in iPhone OS 3.0, the Map Kit framework lets you embed a fully functional map interface into your application window. The map support provided by this framework includes many of the features normally found in the Maps application. You can display standard street-level map information, satellite imagery, or a combination of the two. You can zoom and pan the map programmatically, and the framework provides automatic support for the touch events that let users zoom and pan the map. You can annotate the map with custom information. And you can use the reverse geocoder feature of the framework to find the address associated with map coordinates.

To use the features of the Map Kit framework, you must add MapKit.framework to your Xcode project and link against it in any relevant targets. To access the classes and headers of the framework, include an #import <MapKit/MapKit.h> statement at the top of any relevant source files.

Adding a Map View to Your User Interface

To add maps to your application, you embed an instance of the MKMapView class into your application’s view hierarchy. This class provides support both for displaying the map information and for managing the user interactions with that information. You can create an instance of this class programmatically (initializing it with the initWithFrame: method) or use Interface Builder to add it to one of your nib files.

Because it is a view, you can use the frame property of a map view to move and resize it however you like within your view hierarchy. Although the map view does not have any controls of its own, you can layer toolbars and other views on top of it to add controls for interacting with the map contents. Any subviews you add to the map view remain fixed in place and do not scroll with the map contents. If you want to add custom content to the map itself, and have that content scroll with the map, you must create annotations as described in “Displaying Annotations”.

The MKMapView class has a handful of properties that you can configure before displaying it. The most important property to set is the region property. This property defines the portion of the map that should be displayed initially and is how you zoom and pan the contents of the map.

Zooming and Panning the Map Content

The region property of the MK MapView class controls the portion of the map that is displayed at any given time. When you want to zoom or pan the map, all you have to do is change the values in this property appropriately. The property consists of an MK Coordinate Region structure, which has the following definition:

To pan the map to a new location, you change the values in the center field. To zoom in and out, you change the values in the span field. You specify the values for these fields using map coordinates, which are measured in degrees, minutes, and seconds. For the span field, the values you specify represent latitudinal and longitudinal distances. Although latitudinal distances are relatively fixed at approximately 111 kilometers per degree, longitudinal distances vary with the latitude. At the equator, longitudinal values are approximately 111 kilometers per degree but at the poles they approach zero. Of course, you can always use the MK Coordinate Region Make With Distance function to create a region using kilometer values instead of degrees.

To update the map without animating your changes, you can modify the region or center Coordinate property directly. To animate your changes, you must use either the set Region: animated: or set Center Coordinate: animated: method. The set Center Coordinate: animated: method lets you pan the map without zooming in or out inadvertently. The setRegion: animated: method lets you pan and zoom at the same time. For example, to pan the map to the left by half the current map width, you could use the following code to find the coordinate at the left edge of the map and use that as the new center point, as shown here:

To zoom in and out, instead of modifying the center coordinate, you modify the span. To zoom in, you decrease the span. To zoom out, you increase it. In other words if the current span is one degree, specifying a span of two degrees zooms out by a factor of two:

MKCoordinateRegion theRegion = myMapView.region;

Displaying the User’s Current Location

The Map Kit framework includes built-in support for displaying the user’s current location on the map. To show this location, set the shows User Location property of your map view object to YES. Doing so cause the map view to use the Core Location framework to find the user’s location and add an annotation of type MKUserLocation to the map.

The addition of the MK User Location annotation object to the map is reported by the delegate the same way custom annotations are. If you want to associate a custom annotation view with the user’s location, you should return that view from your delegate object’s mapView: view For Annotation: method. If you want to use the default annotation view, you should return nil from that method instead.

Converting Between Coordinates and Pixels

Although you normally specify points on the map using latitude and longitude values, there may be times when you need to convert to and from pixels in the map view object itself. For example, if you allow the user to drag annotations around the map surface, the event handlers for your custom annotation views would need to convert frame coordinates to map coordinates so that they could update the associated annotation object. The MK MapView class includes several routines for converting back and forth between map coordinates and the local coordinate system of the map view object itself, including:

##STRART## convertCoordinate:toPointToView: convertPoint:toCoordinateFromView: convertRegion:toRectToView: convertRect:toRegionFromView:

Displaying Annotations

Annotations are pieces of map content that you define and layer on top of the map itself. The Map Kit framework implements annotations in two parts: an annotation object and a view to display that annotation.

For the most part, you are responsible for providing these custom objects. However, the framework does provide standard annotations and views that you can use as well. Displaying annotations in your map view is a two-step process:

  1. Create your annotation object and add it to your map view.
  2. Implement the mapView:viewForAnnotation: method of your delegate object and create the corresponding annotation view there.

An annotation object is any object that conforms to the MKAnnotation protocol. Typically annotation objects are relatively small data objects that store the coordinate of the annotation and the relevant information about the annotation, such as its name. Because annotations are defined using a protocol, any object in your application can become one. In practice though, you should make your annotation objects lightweight, because the map view keeps references to them until you remove them explicitly; however, the same is not necessarily true of annotation views.

When an annotation needs to be displayed on screen, the map view is responsible for making sure the annotation object has an associated annotation view. It does this by calling the the mapView: view For Annotation: method of its delegate object when the annotation’s coordinate is about to become visible on the screen. Because annotation views tend to be more heavy weight objects than their corresponding annotation objects, though, the map view tries not to keep too many annotation views around in memory at the same time. To do this, it implements a recycling scheme for annotation views that is similar to how table views recycle table cells during scrolling. When an annotation view moves off screen, the map view can disassociate that view from its annotation object and put it on a reuse queue. Before creating a new annotation view object, your delegate’s mapView: view For Annotation: method should always check this reuse queue to see if an existing view is available by calling the map view’s dequeue Reusable Annotation View With Identifier: method. If that method returns a valid view object, you can reinitialize the view and return it; otherwise, you should create and return a new view object.

Adding and Removing Annotation Objects

You do not add annotation views to the map directly. Instead, you add annotation objects, which are typically not views. An annotation object is any object in your application that conforms to the MKAnnotation protocol. The most important part of any annotation object is its coordinate property, which is a required property of the MKAnnotation protocol and provides the anchor point for the annotation on the map.

To add an annotation to the map view, all you have to do is call the add Annotation: or add Annotations: method of the map view object. It is up to you to decide when it is appropriate to add annotations to the map view and to provide the user interface for doing so. You can provide a toolbar with commands that allow the user to create new annotations or you can create the annotations programmatically yourself, perhaps using a local or remote database of information.

If your application needs to dispose of an old annotation, you should always remove it from the map using the remove Annotation: or remove Annotations: method before deleting it. Because the map view shows all annotations that it knows about, you need to remove annotations explicitly if you do not want them displayed on the map. For example, if your application allows the user to filter a list of restaurants or local sights, you would need to remove any annotations that did not match the filter criteria.

Defining Annotation Views

The Map Kit framework provides two annotation view classes: MK Annotation View and MKP in Annotation View. The MK Annotation View class is a concrete view that defines the basic behavior for all annotation views. The MK Pin Annotation View class is a subclass of MK Annotation View that displays one of the standard system pin images at the associated annotation’s coordinate point.

You can use the MK Annotation View class as is to display simple annotations or you can subclass it to provide more interactive behavior. When using the class as is, you provide a custom image to represent the content you want to display on the map and assign it to the image property of the annotation view. Using the class this way is perfect for situations where you do not display dynamically changing content and do not support any user interactivity. However, if you do want dynamic content or user interactivity, you must define a custom subclass.

In a custom subclass, you can draw dynamic content in one of two ways. You can continue to use the image property to display images for the annotation, perhaps setting up a timer so that you can change the current image at regular intervals. You can also override the view’s drawRect: method and draw your content explicitly, again setting up a timer so that you can call the view’s set Needs Display method periodically.

When drawing content using the drawRect: method, you must always remember to specify the size of your annotation view shortly after initialization. The default initialization method for annotation views does not take a frame rectangle as a parameter. Instead, it uses the image you specify in the image property to set that frame size later. If you do not set an image, though, you must set the frame size explicitly in order for your rendered content to be visible.

Creating Annotation Views

You always create annotation views in the mapView:viewForAnnotation: method of your delegate object. Before creating a new view, you should always check to see if there is an existing view waiting to be used by calling the dequeue Reusable Annotation View With Identifier: method. If this method returns a non-nil value, you should assign the annotation provided by the map view to the view’s annotation property, perform any additional configuration needed to put the view in a known state, and return it. If the method returns nil, you should proceed with creating and returning a new annotation view object.

Listing shows a sample implementation of the mapView: view For Annotation: method. This method provides pin annotation views for custom annotation objects. If an existing pin annotation view already exists, this method associates the annotation object to that view. If no view is in the reuse queue, this method creates a new one, setting up the basic properties of the view and configuring an accessory view for the annotation’s callout.

Creating annotation views

Handling Events in an Annotation View

Although they live in a special layer above the map content, annotation views are full-fledged views capable of receiving touch events. You can use these events to provide more user interactivity for your annotations. For example, you could use touch events in the view to drag the view around the surface of the map.

The following sequence of code listings show you how to implement a user-movable annotation view. The annotation view displays a bullseye image directly over the annotation’s coordinate point and includes a custom accessory view for displaying details about the target. Figure shows an instance of this annotation view, along with its callout bubble.

The bullseye annotation view

The bullseye annotation view

Listing shows the definition of the Bulls eye Annotation View class. The class includes some additional member variables that it uses during tracking to move the view correctly. It also stores a pointer to the map view itself, the value for which is set by the code in the mapView: view For Annotation: method when it creates or reinitializes the annotation view. The map view object is needed when event tracking is finished to adjust the map coordinate of the annotation object.

The BullseyeAnnotationView class

When a touch event first arrives in a bullseye view, the touches Began: with Event: method of that class records information about the initial touch event, as shown in Listing. It uses this information later in its touches Moved: with Event: method to adjust the position of the view. All location information is stored in the coordinate space of the superview.

Tracking the view’s location

@implementation BullseyeAnnotationView (TouchBeginMethods) - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// The view is configured for single touches only. UITouch* aTouch = [touches anyObject]; startLocation = [aTouch locationInView:[self superview]]; originalCenter = self.center; [super touchesBegan:touches withEvent:event]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* aTouch = [touches anyObject]; CGPoint newLocation = [aTouch locationInView:[self superview]]; CGPoint newCenter; // If the user's finger moved more than 5 pixels, begin the drag. if ( (abs(newLocation.x - startLocation.x) > 5.0) || (abs(newLocation.y - startLocation.y) > 5.0) ) isMoving = YES; // If dragging has begun, adjust the position of the view. if (isMoving) { newCenter.x = originalCenter.x + (newLocation.x - startLocation.x); newCenter.y = originalCenter.y + (newLocation.y - startLocation.y); self.center = newCenter; } else // Let the parent class handle it. [super touchesMoved:touches withEvent:event]; } @end

When the user stops dragging an annotation view, you need to adjust the coordinate of the original annotation to ensure the view remains in the new position. Listing shows the touchesEnded:withEvent: method for the Bulls eye Annotation View class. This method uses the map member variable to convert the pixel-based point into a map coordinate value. Because the coordinate property of an annotation is normally read-only, the annotation object in this case implements a custom change Coordinate method to update the value it stores locally and reports using the coordinate property. If the touch event was cancelled for some reason, the touches Cancelled: with Event: method returns the annotation view to its original position.

Handling the final touch events

Getting Placemark Information from the Reverse Geocoder

The Map Kit framework deals primarily with map coordinate values, which consist of a latitude and longitude pair. Although map coordinates are good for code, they are not always the easiest things for users to understand. To make it easier for users, you can use the MK Reverse Geocoder class to obtain placemark information such as the street address, city, state, and country associated with a given map coordinate.

The MK Reverse Geocoder class queries the underlying map service for information about the map coordinate you specify. Because it requires access to the network, a reverse geocoder object always performs its query asynchronously and reports its results back using the associated delegate object. The delegate for a reverse geocoder must conform to the MK Reverse Geocoder Delegate protocol.

To start a reverse geocoder query, create an instance of the MK Reverse Geocoder, class, assign an appropriate object to the delegate property, and call the start method. If the query completes successfully, your delegate receives an MK Placemark object with the results. Placemark objects are themselves annotation objects—that is, they adopt the MK Annotation protocol—so you can add them to your map view’s list of annotations if you want.

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status