一、使用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 = , // 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] >= ) {
[_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: longitude:];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{
//设置地图中心方式设置经度、纬度
CLLocationCoordinate2D center = {latitude,longitude};
//也可以使用如下方法设置经度、纬度
//center.latitude = latitude;
//center.longitude = longitude;
//设置地图显示范围
MKCoordinateSpan span;
span.latitudeDelta = ;
span.longitudeDelta = ;
//创建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] >= ) {
[_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: longitude:];
CLLocationCoordinate2D to = {,};
CLLocationCoordinate2D from = {,};
MKMapCamera* camera = [MKMapCamera cameraLookingAtCenterCoordinate:to fromEyeCoordinate:from eyeAltitude:];
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 = ;
span.longitudeDelta = ;
//创建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对象,该对象代表一个定位点,该对象包含如下属性:
()、@property (nonatomic, readonly, copy) CLLocation *location;//该只读属性返回一个CLLocation类型的值,该值封装了CLPlacemark对象代表经度、纬度信息;
()、@property (nonatomic, readonly, copy) NSString *name;//该只读属性返回CLPlacemark所代表地址的名称;
()、@property (nonatomic, readonly, copy) NSDictionary *addressDictionary;该只读属性返回一个NSDictionary对象,该封装了CLPlacemark所代表地址的详情;
()、@property (nonatomic, readonly, copy) NSString *ISOcountryCode;//该只读属性返回CLPlacemark所代表地址所在国家的代码;
()、@property (nonatomic, readonly, copy) NSString *country;//该只读属性返回CLPlacemark所代表地址所在的国家;
()、@property (nonatomic, readonly, copy) NSString *postalCode;//该只读属性返回CLPlacemark所代表地址的编码;
()、@property (nonatomic, readonly, copy) NSString *administrativeArea;//该只读属性返回CLPlacemark所代表地址的行政区域;
()、@property (nonatomic, readonly, copy) NSString *subAdministrativeArea;//该只读属性返回CLPlacemark所代表地址的次级行政区域;
()、@property (nonatomic, readonly, copy) NSString *locality;//该只读属性返回CLPlacemark所代表地址的城市名;
()、@property (nonatomic, readonly, copy) NSString *subLocality;//该只读属性返回CLPlacemark所代表地址的下一级城市名;
()、@property (nonatomic, readonly, copy) NSString *thoroughfare;//该只读属性返回CLPlacemark所代表地址的道路名;
()、@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 > ) {
//只处理一个解析结果,实际项目中可使用列表让用户选择;
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: longitude:];
[self.geocoder_02 reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
//如果解析结果的结合元素个数大于0,则表明解析得到了地址信息
if (placemarks.count > ) {
//只处理一个解析结果,实际项目中可使用列表让用户选择;
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、添加简单的锚点
上面方法中MKAnnotation是一个协议,该协议中定义了3个属性,用于设置和返回锚点的信息;
()、@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;//用于设置和返回锚点的位置。该属性值必须是一个CLLocationCoordinate2D结构体变量,封装了经度、纬度信息;
()、@property (nonatomic, readonly, copy) NSString *title;//用于设置和返回锚点的标题;
()、@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] >= ) {
[_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: longitude:];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{
//设置地图中心方式设置经度、纬度
CLLocationCoordinate2D center = {latitude,longitude};
//也可以使用如下方法设置经度、纬度
//center.latitude = latitude;
//center.longitude = longitude;
//设置地图显示范围
MKCoordinateSpan span;
span.latitudeDelta = ;
span.longitudeDelta = ;
//创建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的
方法,该方法的返回值将作为地图上的描点控件;
例如:
#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] >= ) {
[_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: longitude:];
}
- (void)locateToLatitude:(CGFloat)latitude longitude:(CGFloat)longitude{
//设置地图中心方式设置经度、纬度
CLLocationCoordinate2D center = {latitude,longitude};
//也可以使用如下方法设置经度、纬度
//center.latitude = latitude;
//center.longitude = longitude;
//设置地图显示范围
MKCoordinateSpan span;
span.latitudeDelta = ;
span.longitudeDelta = ;
//创建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提供了如下方法来添加覆盖层:
()、- (void)addOverlay:(id <MKOverlay>)overlay level:(MKOverlayLevel)level NS_AVAILABLE(_9, _0);//将单个的覆盖层添加到指定的层级;
()、- (void)addOverlays:(NSArray *)overlays level:(MKOverlayLevel)level NS_AVAILABLE(_9, _0);//将多个覆盖层添加到指定的层级;
()、- (void)removeOverlay:(id <MKOverlay>)overlay NS_AVAILABLE(_9, _0);//将单个的覆盖层添加到默认层级;
()、- (void)removeOverlays:(NSArray *)overlays NS_AVAILABLE(_9, _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步即可。
- 创建MKOverlay的实现类的实例,并设置必要的信息;
- 调用MKMapView的添加层的方法将MKOverlay添加进入;
- 重写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] >= ) {
[_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: longitude:];
//创建一个长按手势
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 = ;
span.longitudeDelta = ;
//创建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:];
//添加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 = ;
//设置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] >= ) {
[_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: longitude:];
//创建一个长按手势
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 = ;
span.longitudeDelta = ;
//创建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.3
renderer.alpha = ;
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 = << ,//汽车
MKDirectionsTransportTypeWalking = << ,//步行
MKDirectionsTransportTypeAny = x0FFFFFFF//任意
} NS_ENUM_AVAILABLE(, );
- (2)、以MKDirectionsRequest为参数,创建MKDirections对象;
- (3)、调用MKDirections的如下放方法请求导航路线。调用该方法时需要传入一个代码块——当该对象成功获取导航路线之后将会激发该代码块。
行车导航仪
#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] >= ) {
[_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: longitude:];
self.geocoder = [[CLGeocoder alloc]init];
//解析目标地址,获取实际的经度、纬度信息
[self.geocoder geocodeAddressString:@"北京市朝阳区华悦国际" completionHandler:^(NSArray *placemarks, NSError *error) {
/**
* 在实际应用中,此处如果发西安placemarkd集合包含多个元素,即标明根据该地址字符串检索到多个地址值,那么应该显示一个列表框让用户选择目标地址,此处为了简化该示例,直接使用第一个地址值作为目标地址
*/
if (placemarks.count > ) {
//删除上一条的导航路线
[self.mapView removeOverlay:self.naviPath];
//创建MKDirectionsReqyest对象,作为查询导航路线的请求
MKDirectionsRequest* request = [[MKDirectionsRequest alloc]init];
//将当前位置设置为导航的起始点
request.source = [MKMapItem mapItemForCurrentLocation];
//获取地址解析得到的一个地址值
CLPlacemark* clPlacemark = placemarks[];
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 > ) {
//获取查询得到的导航信息
MKRoute* route = response.routes[];
//保存检索得到的导航信息
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 = ;
span.longitudeDelta = ;
//创建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 = ;
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类,通过该类可以获取到啊好难过路线信息。