iOS开发之-Map

Displaying Maps


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


Understanding Map Geometry

Map Coordinate Systems

In order to understand the coordinate systems used by Map Kit, it helps to understand how the three-dimensional surface of the Earth is mapped to a two-dimensional map. 




How you specify data points on a map depends on how you intend to use them. Map Kit supports three basic coordinate systems for specifying map data points:

  • A map coordinate is a latitude and longitude on the spherical representation of the Earth. Map coordinates are the primary way of specifying locations on the globe. You specify individual map coordinate values using theCLLocationCoordinate2D structure. You can specify areas using theMKCoordinateSpan and MKCoordinateRegion structures.
  • A map point is an x and y value on the Mercator map projection. Map points are used for many map-related calculations instead of map coordinates because they simplify the mathematics involved in the calculations. You specify individual map points using the MKMapPoint structure. You can specify areas using theMKMapSize and MKMapRect structures.
  • A point is a graphical unit associated with the coordinate system of aUIView object. Map points and map coordinates must be mapped to points before drawing custom content in a view. You specify individual points using theCGPoint structure. You can specify areas using the CGSize and CGRect structures.

Converting Between Coordinate Systems




Adding a Map View to Your User Interface

You should never subclass MKMapView but should only embed it as-is into your application’s view hierarchy. You should also assign a delegate object to the map. The map view reports all relevant interactions to its delegate so that it has a chance to respond appropriately.

You can add a map view to your application programmatically or using Interface Builder:

  • To add a map using Interface Builder, drag a Map view object to the appropriate view or window.
  • To add a map programmatically, create an instance of theMKMapView class, initialize it using theinitWithFrame: method, and then add it as a subview to your view hierarchy.

Because it is a view, you can manipulate a map view in the same ways you manipulate other views. Unlike a view, you never handle touch events directly in a map view. The map view itself is an opaque container for a complex view hierarchy that handles the display of map-related data and all interactions with that data. Any subviews you add to the map view retain the position specified by theirframe property and do not scroll with the map contents.

New maps are configured to accept user interactions and display map data only. You can configure the map to display satellite imagery or a mixture of satellite and map data by changing the value in themapType property. If you want to limit user interactions, you can change the values in thezoomEnabled and scrollEnabled properties as well.


Configuring the Properties of a Map

Setting the Visible Portion of the Map

The region property of the MKMapView class controls the currently visible portion of the map. When it is first created, a map’s visible region is typically set to the entire world. 


typedef struct {
   CLLocationCoordinate2D center;
   MKCoordinateSpan span;
} MKCoordinateRegion;


The span is analogous to the width and height values of a rectangle but is specified using map coordinates and thus is measured in degrees, minutes, and seconds. One degree of latitude is equivalent to approximately 111 kilometers but longitudinal distances vary with the latitude. If you prefer to specify the span using meters, you can use the MKCoordinateRegionMakeWithDistance to create a region data structure using meter values instead of degrees.

The value you assign to the region property is usually not the same value that is eventually stored by that property. Setting the span of a region nominally defines the rectangle you want to view but also implicitly sets the zoom level for the map view itself. The map view cannot display arbitrary zoom levels and must adjust any regions you specify to match the zoom levels it supports. It chooses the zoom level that allows your entire region to be visible while still filling as much of the screen as possible. It then adjust the region property accordingly. To find out the resulting region without actually changing the value in the region property, you can use the regionThatFits: method of the map view.


Zooming and Panning the Map Content


Zooming and panning allow you to change the visible portion of the map at any time:

Using the centerCoordinate property (or the setCenterCoordinate:animated: method) lets the map view know that it should leave the zoom level unchanged and update the span as needed. 


CLLocationCoordinate2D mapCenter = myMapView.centerCoordinate;
mapCenter = [myMapView convertPoint:
               CGPointMake(1, (myMapView.frame.size.height/2.0))
               toCoordinateFromView:myMapView];
[myMapView setCenterCoordinate:mapCenter animated:YES];

To zoom the map, modify the span of the visible map region. To zoom in, assign a smaller value to the span. To zoom out, assign a larger value. 


MKCoordinateRegion theRegion = myMapView.region;

// Zoom out
theRegion.span.longitudeDelta *= 2.0;
theRegion.span.latitudeDelta *= 2.0;
[myMapView setRegion:theRegion animated:YES];

Displaying the User’s Current Location on the Map


To show this location, set the showsUserLocation property of your map view object to YES. Doing so causes the map view to use Core Location to find the user’s location and add an annotation of type MKUserLocation to the map.

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:viewForAnnotation: method. 


Responding to User Interactions with a Map

The MKMapView class reports significant map-related events to its associated delegate object. The delegate object is an object that conforms to the MKMapViewDelegate protocol. You provide this object in situations where you want to respond to the following types of events:

  • Changes to the visible region of the map
  • The loading of map tiles from the network
  • Changes in the user’s location
  • Changes associated with annotations and overlays.

Annotating Maps

Although the map itself is scrollable, any subviews you add to a map view remain fixed in place and do not scroll. If you want to affix content to the map itself, and thus have that content scroll along with the rest of the map, you must use annotations and overlays.

Annotations are used to display content that can be defined by a single coordinate point. By contrast, overlays are used to display content that is defined by any number of points and may constitute one or more contiguous or noncontiguous shapes. You use overlays to present more complex information such as traffic information, the boundaries of parks, lakes, cities, states, countries, or other bounded areas.


Adding Annotations to a Map




In order to display an annotation on a map, your application must provide two distinct objects:

  • An object that conforms to the MKAnnotation protocol and manages the data for the annotation. (This object is the annotation object.)
  • A view (derived from the MKAnnotationView class) used to draw the visual representation of the annotation on the map surface. (This is the annotation view.)

Annotation objects are typically small data objects that store the map coordinate data and any other relevant information about the annotation, such as a title string.The map view keeps a reference to the annotation objects you add to it and uses the data in those objects to determine when to display the corresponding view.

Map Kit provides some standard annotation views and you can also define custom annotation views if you want. However, you do not add annotation views directly to the map surface. Instead, you provide an annotation view when asked for it and let the map view incorporate that view into its opaque view hierarchy. You provide the annotation view using your map view delegate object.


Checklist for Adding an Annotation to the Map

  1. Define an appropriate annotation object using one of the following options:
    • Use the MKPointAnnotation class to implement a simple annotation. This type of annotation contains properties for specifying the title and subtitle strings to display in the annotation’s onscreen callout bubble.
    • Define a custom object that conforms to the MKAnnotation protocol. This type of annotation can store any type of data you want.
  2. Define an annotation view to present the data on screen. How you define your annotation view depends on your needs and may be one of the following:
    • If the annotation can be represented by a static image, create an instance of the MKAnnotationView class and assign the image to its image property
    • If you want to use a standard pin annotation, create an instance of the MKPinAnnotationView class;
    • If a static image is insufficient for representing your annotation, subclass MKAnnotationView and implement the custom drawing code needed to present it. 
  3. Implement the mapView:viewForAnnotation: method in your map view delegate. Your implementation of this method should dequeue an existing annotation view if one exists or create a new one. If your application supports multiple types of annotations, you must include logic in this method to create a view of the appropriate type for the provided annotation object. 
  4. Add your annotation object to the map view using the addAnnotation: or addAnnotations: method.


All annotations are drawn at the same scale every time, regardless of the map’s current zoom level. To counter this behavior, you can add and remove annotations based on the map’s current zoom level. Implementing the logic necessary to add and remove annotations is your responsibility.


Defining a Custom Annotation Object

The most important part of an annotation is the annotation object, which is an object that conforms to the MKAnnotation protocol. If all you want to do is associate a title with a map coordinate, you can use the MKPointAnnotation class for your annotation object. However, if you need to represent additional information with the annotation, you need to define a custom annotation object.


Listing 5-1  Creating a simple annotation object
@@interface MyCustomAnnotation : NSObject <MKAnnotation> {
    CLLocationCoordinate2D coordinate;
}
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithLocation:(CLLocationCoordinate2D)coord;
 
// Other methods and properties.
@end

@implementation MyCustomAnnotation
@synthesize coordinate;
 
- (id)initWithLocation:(CLLocationCoordinate2D)coord {
    self = [super init];
    if (self) {
        coordinate = coord;
    }
    return self;
}
@end

Important: When you implement the coordinate property in your class, it is recommended that you synthesize its creation. IMap Kit uses KVO notifications to detect changes to the coordinate, title, and subtitle properties of your annotations and make any needed changes to the map display.


Using the Standard Annotation Views

The MKAnnotationView class is a concrete view that defines the basic behavior for all annotation views. The MKPinAnnotationView class is a subclass of MKAnnotationView that displays one of the standard system pin images at the associated annotation’s coordinate point.

After creating an instance of this class, assign your custom image to the image property of the object. When the annotation is displayed, the image is displayed centered over the target map coordinate. If you do not want the image to be centered on the map coordinate, you can use the centerOffset property to move the center point horizontally and vertically in any direction. 


MKAnnotationView* aView = [[[MKAnnotationView alloc] initWithAnnotation:annotation
                                  reuseIdentifier:@"MyCustomAnnotation"] autorelease];
aView.image = [UIImage imageNamed:@"myimage.png"];
aView.centerOffset = CGPointMake(10, -20);

You create the standard annotation views in your delegate’s mapView:viewForAnnotation: method. 


Defining a Custom Annotation View

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
 
@interface MyCustomAnnotationView : MKAnnotationView
{
   // Custom data members
}
// Custom properties and methods.
@end

When drawing content using the drawRect: method, you must always remember to specify a nonzero frame size for 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 property of the view explicitly in order for your rendered content to be visible, as shown in Listing 5-5. Because the view draws in only part of its frame, it also sets its opaque property to NO so that the remaining map content shows through. If you do not do this, the drawing system fills your view with the current background color before calling your drawRect: method.


Listing 5-5  Initializing a custom annotation view
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
    if (self)
    {
        // Set the frame size to the appropriate values.
        CGRect  myFrame = self.frame;
        myFrame.size.width = 40;
        myFrame.size.height = 40;
        self.frame = myFrame;
 
        // The opaque property is YES by default. Setting it to
        // NO allows map content to show through any unrendered
        // parts of your view.
        self.opaque = NO;
    }
    return self;
}

In all other respects, drawing custom content in an annotation view is the same as it is in any view. The system calls your view’s drawRect: method as needed to redraw portions of the view that need it and you can force a redraw operation by calling the setNeedsDisplay or setNeedsDisplayInRect: method of your view at any time. If you want to animate the contents of your view, you need to set up a timer to fire at periodic intervals and update your view.


Creating Annotation Views from Your Delegate Object

When is needs an annotation view, the map view calls the mapView:viewForAnnotation: method of its delegate object. If you do not implement this method, or if you implement it and always return nil, the map view uses a default annotation view, which is typically a pin annotation view. If you want to return annotation views other than the default ones, you need to override the method and create your views there.

Before trying to create a new view in your mapView:viewForAnnotation: method, you should always check to see if a similar annotation view already exists. Like table views, the map view has the option of caching unused annotation views that it is not using. When it does this, it makes the unused views available from the dequeueReusableAnnotationViewWithIdentifier: method. If this method returns a value other than nil, you should update the view’s attributes and return it. If the method returns nil, just create a new instance of the appropriate annotation view class. In both cases, it is your responsibility to take the annotation passed to this method and assign it to your annotation view. You should also use this method to update the view before returning it.


Listing 5-6  Creating annotation views
- (MKAnnotationView *)mapView:(MKMapView *)mapView
                      viewForAnnotation:(id <MKAnnotation>)annotation
{
    // If it's the user location, just return nil.
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;
 
    // Handle any custom annotations.
    if ([annotation isKindOfClass:[MyCustomAnnotation class]])
    {
        // Try to dequeue an existing pin view first.
        MKPinAnnotationView*    pinView = (MKPinAnnotationView*)[mapView
        dequeueReusableAnnotationViewWithIdentifier:@"CustomPinAnnotationView"];
 
        if (!pinView)
        {
            // If an existing pin view was not available, create one.
           pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
                       reuseIdentifier:@"CustomPinAnnotation"]
                             autorelease];
            pinView.pinColor = MKPinAnnotationColorRed;
            pinView.animatesDrop = YES;
            pinView.canShowCallout = YES;
 
            // Add a detail disclosure button to the callout.
            UIButton* rightButton = [UIButton buttonWithType:
                               UIButtonTypeDetailDisclosure];
            [rightButton addTarget:self action:@selector(myShowDetailsMethod:)
                               forControlEvents:UIControlEventTouchUpInside];
            pinView.rightCalloutAccessoryView = rightButton;
        }
        else
            pinView.annotation = annotation;
 
        return pinView;
    }

    return nil;
}


Managing the Map’s Annotation Objects

The map view always tries to display a corresponding annotation view when the given coordinate point is on the screen. 

The only way to eliminate annotation overcrowding is to remove some of the annotation objects from the map view. This typically involves implementing the mapView:regionWillChangeAnimated: and mapView:regionDidChangeAnimated: methods to detect changes in the map zoom level. During a zoom change, you can add or remove annotations as needed based on their proximity to one another. 

In iOS 4.0 and later, Map Kit includes numerous functions to make determining the proximity of map points easier. If you convert the map coordinate of your annotation to the map point coordinate space, you can use the MKMetersBetweenMapPoints method to get absolute distances between two points. You can also use each coordinate as the center of a map rectangle and use the MKMapRectIntersectsRect function to find any intersections. 


Marking Your Annotation View as Draggable

To implement minimal support for dragging, you must do the following:

  • In your annotation objects, implement the setCoordinate: method to allow the map view to update the annotation’s coordinate point.
  • When creating your annotation view, set its draggable property to YES.


When the user touches and holds a draggable annotation view, the map view begins a drag operation for it. As the drag operation progresses, the map view calls the mapView:annotationView:didChangeDragState:fromOldState: method of its delegate to notify it of changes to the drag state of your view. You can use this method to affect or respond to the drag operation.

If you want to animate your view during a drag operation, you can do that by implementing a custom dragState method in your annotation view. As the map view processes drag-related touch events, it updates the dragState property of the affected annotation view. Implementing a custom dragState method gives you a chance to intercept these changes and perform additional actions, such as animate the appearance of your view. 




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值