一、使用MapKit框架

MapKit.framework使用MKMapView类代表地图控件,开发者只要在应用界面上添加并显示控件,该应用就可以增加地图。

MapKitView类的常用属性如下:

(1)、 @property (nonatomic) MKMapType mapType;用于设置和返回地图的类型。该属性支持如下

typedef NS_ENUM(NSUInteger, MKMapType) {MKMapTypeStandard = 0,(标准地图)MKMapTypeSatellite,(卫星地图)MKMapTypeHybrid,(混合地图)MKMapTypeSatelliteFlyover NS_ENUM_AVAILABLE(10_11, 9_0),MKMapTypeHybridFlyover NS_ENUM_AVAILABLE(10_11, 9_0),
} NS_ENUM_AVAILABLE(10_9, 3_0) __WATCHOS_PROHIBITED;

(2)、 @property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;用于设置和返回地图是否可缩进;
(3)、@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;用于设置和返回地图是否可滑动;
(4)、 @property (nonatomic, getter=isRotateEnabled) BOOL rotateEnabled NS_AVAILABLE(10_9, 7_0);用于设置和返回地图是否可旋转;
(5)、 @property (nonatomic) MKCoordinateRegion region;用于设置和返回地图的显示区域;
(6)、 @property (nonatomic) CLLocationCoordinate2D centerCoordinate;用于设置和返回地图的中心位置;
(7)、 @property (nonatomic) MKMapRect visibleMapRect;用于设置和返回地图的显示区域;
(8)、 - (void)addAnnotations:(NSArray

typedef NS_ENUM(NSInteger, MKUserTrackingMode) {
MKUserTrackingModeNone = 0, // the user's location is not followed
MKUserTrackingModeFollow, // the map follows the user's location
MKUserTrackingModeFollowWithHeading, // the map follows the user's location and heading
} NS_ENUM_AVAILABLE(NA, 5_0) __WATCHOS_PROHIBITED;

(13)、 @property (nonatomic, weak, nullable) id < MKMapViewDelegate > delegate;用于为地图控件指定delegate对象,该对象负责处理地图控件的相关事件;

MKMapView类的主要功能如下:

  • (1)、显示地图,例如显示广州市的地图;
  • (2)、提供多种显示方式,比如标准地图、卫星地图、混合地图;
  • (3)、支持地图的缩放操作;
  • (4)、支持在地图上做标记,例如标记疯狂软件教育中心。也可以在地图上添加复杂的覆盖层;
  • (5)、在地图上定位手机当前所在位置;
  • (6)、可以通过经纬度、地址在地图上进行定位;

MKMapView的delegate属性必须是一个实现MKMapViewDelegate协议的对象,实现MKMapViewDelegate协议时可根据需要实现对应的方法;当MKMapView控件发生某些事件时,该控件的delegate对象(实现MKMapViewDelegate协议)将会自动激发响应的方法,对MKMapView上发生的特定事件进行处理。

当MKMapView控件发生下列事件时,都会激发MKMapViewDelegate对象的相应方法:

  • ①、当MKMapView显示区域发生变化时;
  • ②、当MKMapView加载以及加载完成地图数据时;
  • ③、当MKMapView定位用户位置时;
  • ④、当MKMapView添加标注时;
  • ⑤、当MKMapView控件上的标注位置被改变时;
  • ⑥、当MKMapView控件上的标注被选中或取消选中时;
  • ⑦、当MKMapView控件添加覆盖层或显示覆盖层时;

1、使用MKMapView控件

MKMapView控件位于MapKit.framework中,因此为了在iOS应用中使用该控件,需要完成两件事情:

  • ①、为该应用添加MapKit框架;
  • ②、在需要使用MKMapView以及相关类的源文件中使用#import < MapKit/MapKit.h >
  • ③、导入MapKit.framework

2、指定地图显示中心和显示区域

#import "ViewController.h"@interface ViewController ()<CLLocationManagerDelegate,MKMapViewDelegate>
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 设置代理_locationManager.delegate = self;// 3. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];// 4.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 6. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeStandard;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//设置代理self.mapView.delegate = self;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];
}
#pragma mark -- MKMapViewDelegate
//MKMapViewDelegate协议中的方法,当MKMapView显示区域将要发生改变时激发该方法
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated{NSLog(@"地图控件的显示区域将要发生改变");
}
//MKMapViewDelegate协议中的方法,当MKMapView显示区域完成改变时激发该方法
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{NSLog(@"地图控件的显示区域完成改变");
}
//MKMapViewDelegate协议中的方法,当MKMapView开始加载数据时激发该方法
- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView{NSLog(@"地图控件开始加载地图数据");
}
//MKMapViewDelegate协议中的方法,当MKMapView加载完数据时激发该方法
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView{NSLog(@"地图控件加载地图数据完成");
}
//MKMapViewDelegate协议中的方法,当MKMapView加载数据失败时激发该方法
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error{NSLog(@"地图控件加载地图数据发生错误,错误信息:%@",error);
}
//MKMapViewDelegate协议中的方法,当MKMapView开始渲染地图时激发该方法
- (void)mapViewWillStartRenderingMap:(MKMapView *)mapView{NSLog(@"地图控件开始渲染地图");
}
//MKMapViewDelegate协议中的方法,当MKMapView渲染地图完成时激发该方法
- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered{NSLog(@"地图控件渲染地图完成");
}#pragma mark -- CLLocationManagerDelegate
//成功获取定位数据后将会激发该方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{//获取最后一个定位数据CLLocation* location = [locations lastObject];//依次获取CLLocation中封装的经度、纬度、高度、速度、方向等信息NSLog(@"经度:%g,纬度:%g,高度:%g,速度:%g,方向:%g",location.coordinate.latitude,location.coordinate.longitude,location.altitude,location.speed,location.course);
}
//定位失败时激发的方法
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{NSLog(@"定位失败:%@",error);
}
@end
//2016-06-21 15:47:14.975 地图[489:71686] 地图控件的显示区域将要发生改变
//2016-06-21 15:47:14.976 地图[489:71686] 地图控件的显示区域完成改变
//2016-06-21 15:47:14.981 地图[489:71686] 地图控件的显示区域将要发生改变
//2016-06-21 15:47:14.982 地图[489:71686] 地图控件的显示区域完成改变
//2016-06-21 15:47:15.035 地图[489:71686] 地图控件开始渲染地图
//2016-06-21 15:47:15.112 地图[489:71686] 地图控件开始加载地图数据
//2016-06-21 15:47:20.052 地图[489:71686] 经度:40.005,纬度:116.406,高度:47.4084,速度:-1,方向:-1

3、使用iOS 7 新增的MKMapCamera

iOS 7新增了MKMapCamera作为地图的是『视点』,开发者可以使用MKMapCamera对象在地图中增加一个视点,用于模拟从指定位置、指定高度看向地图的某个点从而为地图增加3D体验。

在地图上使用MKMapCamera如下两步:

  • ①、创建一个MKMapCamera对象,创建MKMapCamera时需要指定该摄像头的经度,纬度和俯视高度;
  • ②、为MKMapView的camera属性指定MKMapCamera对象。
#import "ViewController.h"@interface ViewController ()
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeSatellite;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = NO;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];CLLocationCoordinate2D to = {23.126272,113.395568};CLLocationCoordinate2D from = {22.826272,113.295568};MKMapCamera* camera = [MKMapCamera cameraLookingAtCenterCoordinate:to fromEyeCoordinate:from eyeAltitude:10];self.mapView.camera = camera;}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];
}
@end

二、根据地址定位

1、地址解析和方向地址解析

由于iOS地图定位必须根据经纬度来完成,因此如果需要程序根据地址进行定位,则需要先把地址解析成经度,纬度。这里涉及如下两个基本概念:

  • (1)、地址解析:把普通用户能看懂的字符串地址转换成经度、纬度;
  • (2)、反向地址解析:把经度,纬度转换成普通的字符串地址;

iOS为地址解析提供了CLGeocoder该工具类提供了如下3个方法来进行地址解析和方向地址解析:

- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;//根据给定的字符串地址进行解析,解析将会割到该地址对应的经度、纬度信息;- (void)geocodeAddressString:(NSString *)addressString inRegion:(CLRegion *)region completionHandler:(CLGeocodeCompletionHandler)completionHandler;//根据给定的字符串地址进行解析,解析将会得到该地址对应的经度、纬度信息。该方法比前一个方法多指定了一个CLRegion类型参数,该参数代表在某个区域内解析,这样可提高解析结果的准确性;- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;//根据指定的经度、纬度地址反向解析得到字符串地址;

由于上面3个解析方法都是比较耗时的操作,因此iOS将这3个方法都使用代码快设计——当地址解析成功或反向地址解析成功后,系统将会自动执行该方法的第三方参数:代码块,该参数是一个形如如下格式的代码块:

    NSString* text;CLGeocoder *geocoder;[geocoder geocodeAddressString:text completionHandler:^(NSArray *placemarks, NSError *error) {}];

如果解析出现错误,NSError对象就封装了解析过程中的错误信息。如果解析成功,NSError参数为nil,第一个NSArray参数则封装了地址解析或反向地址解析得到的结果。一般来会所,地址解析可能得到多个结果——这是因为全球完全可能有多个同名的地点;但反向地址解析一般只会得到一个结果——因为根据指定经度,纬度得到的地址通常是唯一的。

如果解析成功,NSArray集合的元素为CLPlacemark对象,该对象代表一个定位点,该对象包含如下属性:

(1)、@property (nonatomic, readonly, copy) CLLocation *location;//该只读属性返回一个CLLocation类型的值,该值封装了CLPlacemark对象代表经度、纬度信息;(2)、@property (nonatomic, readonly, copy) NSString *name;//该只读属性返回CLPlacemark所代表地址的名称;(3)、@property (nonatomic, readonly, copy) NSDictionary *addressDictionary;该只读属性返回一个NSDictionary对象,该封装了CLPlacemark所代表地址的详情;(4)、@property (nonatomic, readonly, copy) NSString *ISOcountryCode;//该只读属性返回CLPlacemark所代表地址所在国家的代码;(5)、@property (nonatomic, readonly, copy) NSString *country;//该只读属性返回CLPlacemark所代表地址所在的国家;(6)、@property (nonatomic, readonly, copy) NSString *postalCode;//该只读属性返回CLPlacemark所代表地址的编码;(7)、@property (nonatomic, readonly, copy) NSString *administrativeArea;//该只读属性返回CLPlacemark所代表地址的行政区域;(8)、@property (nonatomic, readonly, copy) NSString *subAdministrativeArea;//该只读属性返回CLPlacemark所代表地址的次级行政区域;(9)、@property (nonatomic, readonly, copy) NSString *locality;//该只读属性返回CLPlacemark所代表地址的城市名;(10)、@property (nonatomic, readonly, copy) NSString *subLocality;//该只读属性返回CLPlacemark所代表地址的下一级城市名;(11)、@property (nonatomic, readonly, copy) NSString *thoroughfare;//该只读属性返回CLPlacemark所代表地址的道路名;(12)、@property (nonatomic, readonly, copy) NSString *subThoroughfare;//该只读属性返回CLPlacemark所代表地址的下一级道路名;

地址解析

self.geocoder_01 = [[CLGeocoder alloc]init];
[self.geocoder_01 geocodeAddressString:@"北京市海淀区田村40号院" completionHandler:^(NSArray *placemarks, NSError *error) {//如果解析结果的结合元素个数大于0,则表明解析得到了经度 纬度信息if (placemarks.count > 0) {//只处理一个解析结果,实际项目中可使用列表让用户选择;for (CLPlacemark* placemark in placemarks) {NSLog(@"经度为:%g,纬度为:%g",placemark.location.coordinate.longitude,placemark.location.coordinate.latitude);}}else{NSLog(@"error = %@",error);NSLog(@"无结果");}
}];

反向地址解析

self.geocoder_02 = [[CLGeocoder alloc]init];
CLLocation* location = [[CLLocation alloc]initWithLatitude:39.9286 longitude:116.327];
[self.geocoder_02 reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {//如果解析结果的结合元素个数大于0,则表明解析得到了地址信息if (placemarks.count > 0) {//只处理一个解析结果,实际项目中可使用列表让用户选择;for (CLPlacemark* placemark in placemarks) {NSArray* addrArray = [placemark.addressDictionary objectForKey:@"FormattedAddressLines"];NSMutableString* addr = [NSMutableString string];for (NSString* str in addrArray) {[addr appendString:str];}NSLog(@"%@",placemark.addressDictionary);NSLog(@"经度为:%g,纬度为:%g,地址为:%@",placemark.location.coordinate.longitude,placemark.location.coordinate.latitude,addr);}}else{NSLog(@"error = %@",error);NSLog(@"无结果");}
}];
经度为:116.333,纬度为:39.9299,地址为:中国北京市海淀区甘家口街道百万庄大街22-2号楼

注意
①、CLGeocoder属于CoreLocation.framework,
开发者需要为项目添加CoreLocation框架
②、在使用CLGeocoder的代码中使用#import < CoreLocation/CoreLocation.h >

2、根据地址定位

根据地址定位的思路非常简单,只要如下两步即可:

  • ①、使用CLGeocoder根据字符串地址得到该地址的经度、纬度;
  • ②、根据解析得到的经度、纬度进行定位;

三、在地图上添加锚点

很多时候,我们希望在地图上添加一些锚点(就是一个图标,当用户点击图标时显示该地点的详细信息)来标识重要的地点,只要调用MKMapView的addAnnotation:方法即可为地图添加锚点。

1、添加简单的锚点

- (void)addAnnotation:(id <MKAnnotation>)annotation;

上面方法中MKAnnotation是一个协议,该协议中定义了3个属性,用于设置和返回锚点的信息;

(1)、@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;//用于设置和返回锚点的位置。该属性值必须是一个CLLocationCoordinate2D结构体变量,封装了经度、纬度信息;(2)、@property (nonatomic, readonly, copy) NSString *title;//用于设置和返回锚点的标题;(3)、@property (nonatomic, readonly, copy) NSString *subtitle;//用于设置和返回锚点的副标题

iOS为MKAnnotation协议提供了一个实现类:MKPointAnnotation,该实现类实现了MKAnnotation的全部方法,因此通常可使用MKPointAnnotation来添加锚点:

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeSatellite;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];//创建MKPointAnnotation对象——代表一个锚点MKPointAnnotation* annotation = [[MKPointAnnotation alloc]init];annotation.title = @"正标题";annotation.subtitle = @"我是副标题,代表正标题的位置";CLLocationCoordinate2D coordinate = {latitude,longitude};annotation.coordinate = coordinate;//添加锚点[self.mapView addAnnotation:annotation];
}@end

2、添加自定义锚点

对于iOS的地图而言,锚点实际上由两个部分组成:

  • (1)、锚点信息:锚点信息包括锚点的位置、标题、副标题等信息,这些信息由MKAnnotation对象代表;
  • (2)、锚点控件:锚点控件决定地图上显示的锚点外观,包括锚点的图片等;

iOS 为锚点控件提供了MKAnnotationView和MKPinAnnotationView,其中MKPinAnnotationView是MKAnnotationView的子类,而MKAnnotationView继承了UIView——也就是说,它只是一个可视的UI控件。

MKAnnotationView与MKPinAnnotationView的区别在于,MKPinAnnotationView已经为锚点控件选择了图片。因此,如果打算只用MKPinAnnotationView作为锚点控件,那么该锚点控件将总是显示大头针图片,开发者只能修改大头针的颜色;但如果需要自己定制锚点的图片,则可考虑使用MKAnnotationView,该控件允许指定一个image属性,该属性用于定制锚点控件的图片。

定制地图上的锚点,需要完成如下两步:

(1)、为MKMapView指定delegate对象;
(2)、重写delegate的

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation

方法,该方法的返回值将作为地图上的描点控件;

例如:

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate>
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeSatellite;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;self.mapView.delegate = self;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];//创建MKPointAnnotation对象——代表一个锚点MKPointAnnotation* annotation = [[MKPointAnnotation alloc]init];annotation.title = @"正标题";annotation.subtitle = @"我是副标题,代表正标题的位置";CLLocationCoordinate2D coordinate = {latitude,longitude};annotation.coordinate = coordinate;//添加锚点[self.mapView addAnnotation:annotation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{static NSString* annoId = @"fkAnno";//获取可重用的锚点MKAnnotationView* annoView = [mapView dequeueReusableAnnotationViewWithIdentifier:annoId];//如果可重用的锚点控件不存在,则创建新的可重用锚点控件if (!annoView) {annoView = [[MKAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:annoId];/***  如果不想改变锚点控件的图片,只想改变颜色,则可创建MKPinAnnotationVeiw实例,再修改MKPinAnnotationVeiw对象的pinColor属性即可*/}//为锚点控件设置图片annoView.image = [UIImage imageNamed:@"Unknown.png"];//设置该锚点控件是否可显示起泡信息annoView.canShowCallout = YES;//定义一个按钮,用于锚点控件设置附加控件UIButton* button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];//为按钮绑定事件处理方法[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];//可通过锚点控件的rightCalloutAccessoryView、leftCalloutAccessoryView设置附加控件annoView.rightCalloutAccessoryView = button;return annoView;
}
- (void)buttonTapped:(UIButton *)btn{NSLog(@"111");
}

四、在地图上添加覆盖层

除了在地图上添加简单的锚点之外,有些时候我们还希望向地图上添加一些丰富的信息,比如某个公园的边界、汽车经过的路径、汽车导航的路线,这些信息可通过覆盖层来实现。
MKMapView提供了如下方法来添加覆盖层:

(1)、- (void)addOverlay:(id <MKOverlay>)overlay level:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);//将单个的覆盖层添加到指定的层级;(2)、- (void)addOverlays:(NSArray *)overlays level:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);//将多个覆盖层添加到指定的层级;(3)、- (void)removeOverlay:(id <MKOverlay>)overlay NS_AVAILABLE(10_9, 4_0);//将单个的覆盖层添加到默认层级;(4)、- (void)removeOverlays:(NSArray *)overlays NS_AVAILABLE(10_9, 4_0);//将多个覆盖层添加到默认层级;

上面的四个方法中都涉及覆盖层的概念,iOS使用MKOverlay来代表覆盖层。在iOS 7以前,所有的覆盖层都只能添加到默认层级——也就是说,上面4个方法中前两个是iOS 7新增的。从iOS7以后,覆盖层可以被添加到指定的层级,这样就可以将覆盖层放置在相关数据的上面或者下面。

上面四个方法中前两个都需要传入一个MLOverlayLevel类型的参数,该枚举类型支持MKOverlayLevelAboveRoads、MKOverlayLevelAboveLabels两个枚举。

MKOverlay只是一个协议,因此iOS还为该协议提供了如下实现类:
(1)、MKCircle:代表一个圆形覆盖层;
(2)、MKPolygon:代表一个多边形覆盖层,可用于标识公园的边界等;
(3)、MKPolyline:代表一个多线段覆盖层,可用于标识汽车经过的路径、汽车导航的路线等;
(4)、MKTileOverlay:代表使用位图平铺的覆盖层。这是iOS 7新增的API;

覆盖层与地图锚点有相同的设计,每个覆盖层实际上由两部分信息组成:
(1)、覆盖层的位置、集合形状等信息;
(2)、覆盖层控件;

覆盖层与覆盖层控件的对应关系如下:
(1)、MKCircle对应的覆盖层控件为MKCircleView;
(2)、MKPolygon对应的覆盖层控件为MKPolygonView;
(3)、MKPolyline对应的覆盖层控件为MKPolylineView;
(4)、MKTileOverlay是iOS 7新增的,没有对应的覆盖层控件;

iOS 7推荐使用MKXxxRenderer来负责渲染覆盖层控件。

掌握上面这些覆盖层与覆盖层控件、覆盖层Renderer之间的关系之后,接下来即可通过这些API在地图上添加覆盖层。添加覆盖层也很简单,只要如下3步即可。

  1. 创建MKOverlay的实现类的实例,并设置必要的信息;
  2. 调用MKMapView的添加层的方法将MKOverlay添加进入;
  3. 重写MKMapView的delegate的mapView:rendererForOverlay:方法,该放阿飞返回MKOverlayRenderer将会控制生成地图上覆盖层控件;

提示:
如果使用iOS 7以前的版本,不是重写delegate的mapView:rendererForOverlay:方法,而是重写delegate的mapView:viewForOverlay:方法,该方法返回的MKOverlayView将会作为覆盖层控件。从iOS 7开始,该方法过时了,不再推荐使用。

1、添加几何覆盖层

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate>
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeSatellite;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//设置代理self.mapView.delegate = self;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];//创建一个长按手势UILongPressGestureRecognizer* gesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];[self.mapView addGestureRecognizer:gesture];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];
}
- (void)longPress:(UILongPressGestureRecognizer *)gesture{//获取长按点的坐标CGPoint pos = [gesture locationInView:self.mapView];//将长按点的坐标转换成经纬度值CLLocationCoordinate2D coord = [self.mapView convertPoint:pos toCoordinateFromView:self.mapView];//创建MKCircle对象,该对象代表覆盖层MKCircle* circle = [MKCircle circleWithCenterCoordinate:coord radius:100];//添加MKOverlay[self.mapView addOverlay:circle level:MKOverlayLevelAboveLabels];
}
#pragma mark -- MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{MKCircle* circle = (MKCircle *)overlay;//创建一个MKCircleRenderer对象MKCircleRenderer* render = [[MKCircleRenderer alloc]initWithCircle:circle];//设置MKCirleRenderer的透明度render.alpha = 0.3;//设置MKCirleRenderer的填充颜色和边框颜色render.fillColor = [UIColor blueColor];render.strokeColor = [UIColor redColor];return render;
}
@end

2、使用iOS 7新增的MKTileOverlay覆盖层

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate>
{CLLocationManager* _locationManager;
}
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeStandard;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//设置代理self.mapView.delegate = self;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:23.126272 longitude:113.395568];//创建一个长按手势UILongPressGestureRecognizer* gesture = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];[self.mapView addGestureRecognizer:gesture];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];
}
- (void)longPress:(UILongPressGestureRecognizer *)gesture{NSURL* url = [[NSBundle mainBundle]URLForResource:@"Unknown" withExtension:@"png"];//创建MKTileOverlay对象,该对象代表覆盖层MKTileOverlay* overlay = [[MKTileOverlay alloc]initWithURLTemplate:[url description]];//添加MKOverlay[self.mapView addOverlay:overlay];
}
#pragma mark -- MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{//创建MKTileOverlayRenderer对象MKTileOverlayRenderer* renderer = [[MKTileOverlayRenderer alloc]initWithOverlay:(MKTileOverlay *)overlay];//设置MKTileOverlayRenderer的透明度为0.3renderer.alpha = 0.3;return renderer;
}
@end

五、使用iOS 7新增的MKDirections获取导航路线

iOS新增了MKDirections类,该类可通过MKDirectionsRequest向Apple服务器发送导航请求,Apple服务器将会返回一条或多条导航路线,然后通过覆盖层把其中一条或多条导航的路线绘制在地图上,这样就可以提醒用户进行导航。

在最简单的情况下,程序只要简单的显示当前用户的位置,MKMapView上就会显示用户当前已经行走到哪里,这样就可以实现一个简单的导航软件了。

提示:
更实际的导航软件可能会使用汽车图片来代表用户当前位置——这很简单,因为用户当前位置也就是一锚点,可以通过重写MKMapView的delegate的mapView:viewForAnnotation:方法来改变代表用户当前位置的锚点图片。除此之外,还可以通过CLLocationManager监听用户位置的改变,让地图始终以用户当前位置为中心,甚至可通过CLLocationManager获取用户移动的方向,从而控制代表用户当前位置的汽车图标始终以车头向前的方式显示。

使用MKDirections获取导航路线只要如下3步:

  • (1)、创建MKDirectionsRequest对象,并为该对象设置导航起始点、导航结束点等必要信息。除此之外,还可为该对象设置transportType属性,该属性指定用户移动的交通工具,该属性支持如下3个枚举值之一
typedef NS_OPTIONS(NSUInteger, MKDirectionsTransportType) {MKDirectionsTransportTypeAutomobile     = 1 << 0,//汽车MKDirectionsTransportTypeWalking        = 1 << 1,//步行MKDirectionsTransportTypeAny            = 0x0FFFFFFF//任意
} NS_ENUM_AVAILABLE(10_9, 7_0);
  • (2)、以MKDirectionsRequest为参数,创建MKDirections对象;
  • (3)、调用MKDirections的如下放方法请求导航路线。调用该方法时需要传入一个代码块——当该对象成功获取导航路线之后将会激发该代码块。
- (void)calculateDirectionsWithCompletionHandler:(MKDirectionsHandler)completionHandler;

行车导航仪

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate,CLLocationManagerDelegate>
{CLLocationManager* _locationManager;
}
//定义一个CLGeocoder对象,该对象负责对用户输入的地址进行解析
@property (nonatomic ,strong) CLGeocoder* geocoder;
//定义一个变量来保存导航路线
@property (nonatomic ,strong) MKPolyline* naviPath;
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];//如果定位服务可用if ([CLLocationManager locationServicesEnabled]) {// 1. 实例化定位管理器_locationManager = [[CLLocationManager alloc] init];// 2. 定位精度_locationManager.delegate = self;[_locationManager setDesiredAccuracy:kCLLocationAccuracyHundredMeters];// 3.请求用户权限:分为:?只在前台开启定位?在后台也可定位,if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {[_locationManager requestWhenInUseAuthorization];//?只在前台开启定位}// 4. 更新用户位置[_locationManager startUpdatingLocation];}else{NSLog(@"无法使用定位服务!!!");}//设置地图的显示风格,此处设置使用标准地图self.mapView.mapType = MKMapTypeStandard;//设置地图可收缩self.mapView.zoomEnabled = YES;//设置地图可滚动self.mapView.scrollEnabled = YES;//设置地图可旋转self.mapView.rotateEnabled = YES;//设置显示用户当前位置self.mapView.showsUserLocation = YES;//设置代理self.mapView.delegate = self;//调用自己实现的方法设置地图的显示位置和显示区域[self locateToLatitude:40.005 longitude:116.406];self.geocoder = [[CLGeocoder alloc]init];//解析目标地址,获取实际的经度、纬度信息[self.geocoder geocodeAddressString:@"北京市朝阳区华悦国际" completionHandler:^(NSArray *placemarks, NSError *error) {/***  在实际应用中,此处如果发西安placemarkd集合包含多个元素,即标明根据该地址字符串检索到多个地址值,那么应该显示一个列表框让用户选择目标地址,此处为了简化该示例,直接使用第一个地址值作为目标地址*/if (placemarks.count > 0) {//删除上一条的导航路线[self.mapView removeOverlay:self.naviPath];//创建MKDirectionsReqyest对象,作为查询导航路线的请求MKDirectionsRequest* request = [[MKDirectionsRequest alloc]init];//将当前位置设置为导航的起始点request.source = [MKMapItem mapItemForCurrentLocation];//获取地址解析得到的一个地址值CLPlacemark* clPlacemark = placemarks[0];MKPlacemark* mkPlacemark = [[MKPlacemark alloc]initWithPlacemark:clPlacemark];//将解析得到的目标设置为导航的结束点request.destination = [[MKMapItem alloc]initWithPlacemark:mkPlacemark];//以MKDirectionsReqyest作为参数,创建MKDirections对象MKDirections* directions = [[MKDirections alloc]initWithRequest:request];[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {if (response.routes.count > 0) {//获取查询得到的导航信息MKRoute* route = response.routes[0];//保存检索得到的导航信息self.naviPath = route.polyline;//将self.naviPath(MKPolyline对象)添加成覆盖层[self.mapView addOverlay:self.naviPath level:MKOverlayLevelAboveLabels];}}];}}];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{//设置地图中心方式设置经度、纬度CLLocationCoordinate2D center = {latitude,longitude};//也可以使用如下方法设置经度、纬度//center.latitude = latitude;//center.longitude = longitude;//设置地图显示范围MKCoordinateSpan span;span.latitudeDelta = 0.01;span.longitudeDelta = 0.01;//创建MKCoordinateRegion对象,该对象代表地图的显示中心和显示范围MKCoordinateRegion region = {center,span};//设置当前地图的显示中心和显示范围[self.mapView setRegion:region animated:YES];
}#pragma mark -- MKMapViewDelegate
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{//创建MKPolylineRenderer对象,该对象负责绘制MKPolyline覆盖层控件MKPolylineRenderer* renderer = [[MKPolylineRenderer alloc]initWithPolyline:overlay];//设置MKPolylineRenderer对象的线条颜色renderer.strokeColor = [UIColor blueColor];//设置MKPloylineRenderer的线宽度renderer.lineWidth = 2;return renderer;
}
#pragma mark -- CLLocationManagerDelegate
//成功获取定位数据后将会激发该方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{//获取最后一个定位数据CLLocation* location = [locations lastObject];//依次获取CLLocation中封装的经度、纬度、高度、速度、方向等信息NSLog(@"经度:%g,纬度:%g,高度:%g,速度:%g,方向:%g",location.coordinate.latitude,location.coordinate.longitude,location.altitude,location.speed,location.course);
}
//定位失败时激发的方法
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{NSLog(@"定位失败:%@",error);
}
@end

六、本章小结

本章详细介绍了iOS系统的MapKit框架。MapKit框架的主要作用就是在iOS应用中增加地图功能,MapKit框架的基础部分就是MKMapView、MKMapViewDelegate以及MKMapCamera,通过这些API即可在应用中增加地图并处理地图加载的相关事件。除此之外,希望在地图上添加锚点,则需要借助于MKAnnotation协议(包括MKPointAnnotation实现类)、MKAnnotationView和MKPinAnnotationView;如果希望在地图上添加覆盖层,则需要借助于MKOverlay协议(包括各种实现类)和MKOverlayRenderer的各种子类(iOS 7以前使用MKOverlayView以及各种子类);
本章最后还介绍了iOS 7新增的MKDirections类,通过该类可以获取到啊好难过路线信息。

第十章:使用MapKit开发地图服务相关推荐

  1. 使用google map v3 api 开发地图服务

    API学习地址 其实google map的api很简单的,这里是学习文档的传送门http://code.google.com/intl/zh-CN/apis/maps/documentation/ja ...

  2. 【转】GeoServer地图开发解决方案(四):发布Web地图服务(WMS)篇

    GeoServer 是 OpenGIS Web 服务器规范的 J2EE 实现的社区开源项目,利用 GeoServer 可以方便的发布地图数据,允许用户对特征数据进行更新.删除.插入操作,通过 GeoS ...

  3. android 地图服务开发 INSTALL_FAILED_MISSING_SHARED_LIBRARY 错误解决

    android 地图服务开发 INSTALL_FAILED_MISSING_SHARED_LIBRARY 错误解决 地图服务开发中遇到此类错误大体分为以下两种情况解决:(论坛里大多都是属于第一种情况的 ...

  4. wms地图绘制工具_GeoServer地图开发解决方案(四):发布Web地图服务(WMS)篇

    GeoServer 是 OpenGIS Web 服务器规范的 J2EE 实现的社区开源项目,利用 GeoServer 可以方便的发布地图数据,允许用户对特征数据进行更新.删除.插入操作,通过 GeoS ...

  5. GeoServer地图开发解决方案(四):发布Web地图服务(WMS)篇

    GeoServer 是 OpenGIS Web 服务器规范的 J2EE 实现的社区开源项目,利用 GeoServer 可以方便的发布地图数据,允许用户对特征数据进行更新.删除.插入操作,通过 GeoS ...

  6. android 原生开发 3d地图 下载_arcgis api 3.x for js 入门开发系列二不同地图服务展示(附源码下载)...

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  7. Arcgis加载离线地图服务二次开发

    ARCGIS搭建离线地图服务器,进行离线地图二次开发 1.     离线地图金字塔瓦片数据  (下载数据教程:http://www.bigemap.com/helps/doc20190312126.h ...

  8. arcgis api 3.x for js 入门开发系列二不同地图服务展示(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  9. World Wind Java开发之十四——添加WMS地图服务资源(转)

    数据是GIS的核心,没有数据一切无从谈起,Internet上有很多在线WMS地图服务资源,我们可以好好利用这些数据资源,比如天地图.必应地图.NASA.OGC数据服务等等. 在我们国家常用的还是天地图 ...

最新文章

  1. Azure Bill
  2. 4.11 一维到三维推广-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  3. DataScience:深入探讨与分析机器学习中的数据处理之线性变换—标准化standardization、归一化Normalization/比例化Scaling的区别与联系
  4. GDCM:gdcm::LTComp的测试程序
  5. LeetCode 868. 二进制间距(位运算)
  6. html ace编辑器,Tiny-editor
  7. ios弧形进度条_iOS手把手教你实现圆形进度条
  8. [Python]输入与输出
  9. 使用与不使用@RequestBody注解的区别
  10. css 对齐方式 分类
  11. 清华开学!柯洁报到,00后AI明日之星扎堆姚班、智班
  12. Windows配置域名
  13. 宇宙那么大,跟着链游“上天”看看?
  14. iPad/iPhone与电脑共享文件
  15. scala成长之路(1)基本语法和数据类型
  16. 被夸大的伊朗“Twitter革命”
  17. Springcloud+Seata+nacos 分布式事务项目搭建 AT模式
  18. 使用pyecharts绘制地图
  19. 华硕电脑装linux黑屏,华硕电脑更新显卡后开机黑屏应该怎么解决
  20. Android NFC读MifareClassic卡获取卡片ID 类型 扇区 存储空间

热门文章

  1. imac pro 接显示器_您应该购买iMac Pro,还是等待模块化Mac Pro重新设计?
  2. 南大通用GBase8s 常用SQL语句(240)
  3. 前端系列之实战(城市医院预约挂号平台3.UI组件)
  4. linux fpe 错误,linux c++报错不知道原因
  5. 51单片机流水灯的三种实现方法
  6. s.c.普尔著语言学入门,语言学入门
  7. 扩展单精度格式是什么_网络资讯:EPS 是什么
  8. 从进入内核态看内存管理--文末送书
  9. Android手机如何录制屏幕及转GIF
  10. LeetCode(持续更新)