:使用MapKit开发地图服务

Posted xiaoxiaobukuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:使用MapKit开发地图服务相关的知识,希望对你有一定的参考价值。

一、使用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<

以上是关于:使用MapKit开发地图服务的主要内容,如果未能解决你的问题,请参考以下文章

使用 mapkit 创建地图?

使用 Alamofire 和 Mapkit Swift 3 请求

MapKit 帮助。无法先显示我的位置

MapKit 自定义注释被添加到地图,但在地图上不可见

iPhone MapKit 错误

使用 mapKit 的地图显示相反的方向(iOS Swift)