原文地址:http://www.highoncoding.com/Articles/804_Introduction_to_MapKit_Framework_for_iPhone_Development.aspx
地图对我们的生活来说是非常重要的一部分。我们经常用地图来确定位置和方向。这篇文章主要是用来介绍地图框架在ios里面的应用。
关键的概念:
MKMapView:用来在ios设备上显示地图的 UI 控件。
Annotation:用来在地图上展示的点,包括了很多的信息,比如坐标,标题,副标题等。
AnnotationView:定义点的视图部分,由 MKAnnotationView 这个类来定义。
1.参考的框架:
在下面的内容里面,我们将用到MapKit.framework 和 CoreLocation.framework 这个两个框架。在开始项目之前,把这两个框架添加到我们的项目里面。如图所示:
2.通过MKMapView这个UI来展示地图:
在添加这两个框架以后,我们准备开始创建我们的地图套件的应用。
第一步,在XB里面创建项目,拖动MKMapView这个UI。
The MapKitDemoAppDelegate.h 实现如下:
01 | #import <UIKit/UIKit.h> |
02 | #import <MapKit/MapKit.h> |
03 | #import <CoreLocation/CoreLocation.h> |
05 | #import "LameAnnotationView.h" |
07 | @ interface MapKitDemoAppDelegate : NSObject <UIApplicationDelegate,CLLocationManagerDelegate,MKMapViewDelegate,UITextFieldDelegate> { |
09 | CLLocationManager *locationManager; |
10 | IBOutlet MKMapView *mapView; |
14 | @property (nonatomic, retain) IBOutlet UIWindow *window; |
15 | @property (nonatomic,retain) IBOutlet MKMapView *mapView; |
点击Run,我们可以在仿真器上看到这样的结果:
di er bu
第二步:确定当前的位置:
在 MapKitDemoAppDelegate.m里面添加下面的代码:
01 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
03 | self.mapView. delegate = self; |
05 | locationManager = [[CLLocationManager alloc] init]; |
06 | [locationManager setDelegate:self]; |
08 | [locationManager setDistanceFilter:kCLDistanceFilterNone]; |
09 | [locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; |
11 | [self.mapView setShowsUserLocation:YES]; |
15 | [self.window makeKeyAndVisible]; |
CLLocationManage是用来获得用户当前位置的类,下面这行代码使得mapview获得用户当前的位置:
1 | [self.mapView setShowsUserLocation:YES]; |
点击Run,我们可以在仿真器上看到这样的结果:
现在我们能看到用户在地图上被定位,但是现在只是看见了这一个点,我们需要放大这个区域,获得更加具体的信息。通过下面的代码,能够实现这个功能。
在 MapKitDemoAppDelegate.m里面添加下面的代码:
1 | - ( void )mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views |
3 | MKAnnotationView *annotationView = [views objectAtIndex:0]; |
4 | id<MKAnnotation> mp = [annotationView annotation]; |
5 | MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate] ,250,250); |
7 | [mv setRegion:region animated:YES]; |
上面的函数,是MKMapViewDelegate的一种代理的方法,它的功能是发送事件给当前的控制类,但是,我们必须在函数didFinishLaunchingWithOptions method:里面添加下面的代码:
1 | self.mapView. delegate = self; |
我们现在能在地图上看到一个点,MKCoordinateRegion创建了一个初始的区域在地图上,我们可以通过放大和缩小查看这个区域。
仿真器上定位的位置是,苹果公司在美国的位置。你必须在真机上调试才能看到我们当前的位置。
第三步:在地图上添加点:
继承 MKAnnotation这个类,我们创建一个叫做MapPoint的新类。实现如下:
01 | #import <Foundation/Foundation.h> |
02 | #import <CoreLocation/CoreLocation.h> |
03 | #import <MapKit/MapKit.h> |
05 | @ interface MapPoint : NSObject<MKAnnotation> { |
09 | CLLocationCoordinate2D coordinate; |
13 | @property (nonatomic, readonly ) CLLocationCoordinate2D coordinate; |
14 | @property (nonatomic,copy) NSString *title; |
15 | @property (nonatomic,copy) NSString *subTitle; |
17 | -(id) initWithCoordinate:(CLLocationCoordinate2D) c title:(NSString *) t subTitle:(NSString *) st; |
当地图获得新的位置时, MKMapViewDelegate的方法didUpdateUserLocation会被调用。在这个方法里,我们初始化点,并且把他们添加到地图上。
01 | - ( void )mapView:(MKMapView *)mv didUpdateUserLocation:(MKUserLocation *)userLocation |
03 | CLLocationCoordinate2D userCoordinate = userLocation.location.coordinate; |
05 | for ( int i = 1; i<=5;i++) |
07 | CGFloat latDelta = rand()*.035/RAND_MAX -.02; |
08 | CGFloat longDelta = rand()*.03/RAND_MAX -.015; |
10 | CLLocationCoordinate2D newCoord = { userCoordinate.latitude + latDelta, userCoordinate.longitude + longDelta }; |
11 | MapPoint *mp = [[MapPoint alloc] initWithCoordinate:newCoord title:[NSString stringWithFormat: @"Azam Home %d" ,i] subTitle: @"Home Sweet Home" ]; |
12 | [mv addAnnotation:mp]; |
添加随机的点的坐标,效果如下:
如果想要实现callout的功能,比如弹出来的默认的文本框。我们想做的更加的有趣,在callout的左边或者右边添加一个小房子的图片。
第四步:实现默认点的图层:
The leftCalloutAccessoryView and rightCalloutAccessoryView这两个属性用来展示点的默认的属性。下面代码,展示了一张图片是如何在callout里面被显示出来的:
01 | - (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation |
03 | if ([annotation isKindOfClass:[MKUserLocation class ]]) |
06 | NSString *annotationIdentifier = @"PinViewAnnotation" ; |
08 | MKPinAnnotationView *pinView = (MKPinAnnotationView *) [mapView |
09 | dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier]; |
14 | pinView = [[[MKPinAnnotationView alloc] |
15 | initWithAnnotation:annotation |
16 | reuseIdentifier:annotationIdentifier] autorelease]; |
18 | [pinView setPinColor:MKPinAnnotationColorGreen]; |
19 | pinView.animatesDrop = YES; |
20 | pinView.canShowCallout = YES; |
22 | UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed: @"house.png" ]]; |
23 | pinView.leftCalloutAccessoryView = houseIconView; |
24 | [houseIconView release]; |
28 | pinView.annotation = annotation; |
为了提升地图的表现效果,我们用id来保存地图的点的类型。也就是说,同一种点,用在地图的不同部分,重复利用同一种点,而不在重新的创建它。run 代码,在仿真器上点击绿的点,能够看到以下的结果:
上面的显示中,一张图片在点的callout里面显示出来了,但是图片的尺寸太大了。我们可以通过下面的代码调整图片大小:
1 | UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed: @"house.png" ]]; |
2 | [houseIconView setFrame:CGRectMake(0, 0, 30, 30)]; |
3 | pinView.leftCalloutAccessoryView = houseIconView; |
4 | [houseIconView release]; |
尽管我们初始化了点的LameAnnotationViewcallout,我们没有改变点的样子,它是默认的样子。点能够被改变,通过建立一个点的类,重载DrawRect这个方法。继承,我们重新创建一个类,LameAnnotationView。以下是实现的代码:
01 | #import "LameAnnotationView.h" |
03 | @implementation LameAnnotationView |
05 | - (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier |
07 | self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; |
10 | CGRect frame = self.frame; |
11 | frame.size = CGSizeMake(60.0, 85.0); |
13 | self.backgroundColor = [UIColor clearColor]; |
14 | self.centerOffset = CGPointMake(-5, -5); |
19 | -( void ) drawRect:(CGRect)rect |
21 | [[UIImage imageNamed: @"house.png" ] drawInRect:CGRectMake(30, 30.0, 30.0, 30.0)]; |
为了使用我们创建的新点的类,用LameAnnotationView来替代默认的类,代码如下:
01 | - (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation |
03 | if ([annotation isKindOfClass:[MKUserLocation class ]]) |
06 | NSString *annotationIdentifier = @"PinViewAnnotation" ; |
08 | LameAnnotationView *pinView = (LameAnnotationView *) [mapView |
09 | dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier]; |
14 | pinView = [[[LameAnnotationView alloc] |
15 | initWithAnnotation:annotation |
16 | reuseIdentifier:annotationIdentifier] autorelease]; |
20 | pinView.canShowCallout = YES; |
22 | UIImageView *houseIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed: @"house.png" ]]; |
23 | [houseIconView setFrame:CGRectMake(0, 0, 30, 30)]; |
24 | pinView.leftCalloutAccessoryView = houseIconView; |
25 | [houseIconView release]; |
29 | pinView.annotation = annotation; |
我们在初始化点的视图时候,我们忽略了产生点的时候的特效。但是,这个部分的内容,可以通过特效的库,添加进来。以下代码是用来显示点被添加的时候,的效果。
01 | - ( void )mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views |
06 | CGRect visibleRect = [mapView annotationVisibleRect]; |
08 | for (MKAnnotationView *view in views) |
10 | if ([view isKindOfClass:[LameAnnotationView class ]]) |
12 | CGRect endFrame = view.frame; |
14 | CGRect startFrame = endFrame; |
16 | startFrame.origin.y = visibleRect.origin.y - startFrame.size.height; |
17 | view.frame = startFrame; |
20 | [UIView beginAnimations: @"drop" context:NULL]; |
21 | [UIView setAnimationDuration:2]; |
24 | view.frame = endFrame; |
25 | [UIView commitAnimations]; |
30 | MKAnnotationView *annotationView = [views objectAtIndex:0]; |
31 | id<MKAnnotation> mp = [annotationView annotation]; |
32 | MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate] ,5000,5000); |
34 | [mv setRegion:region animated:YES]; |
现在点击run,我们可以看到house点从天而降的下过,这个方法我们在 didAddAnnotationViews 方法里面实现过。
结论:
在这篇文章里面,我们学会了如何使用MapKit。同时,我们还证明了,如何去初始化点,初始化点的视图,特别是视图的显示效果,它能够增加用户的体验。