首页 > 代码库 > iOS核心笔记—MapKit框架-导航
iOS核心笔记—MapKit框架-导航
1、导航简介:
?了解
:导航,简单来说,就是根据用户指定的位置,进行路线规划;然后根据用户在行走过程中,实时的给出指引提示。
1-1、iOS导航实现方案:
方案 | 详细说明 |
---|---|
方案一 | 将需要导航的位置传递给系统的地图APP进行导航 |
方案二 | 发送网络请求到Apple服务器/公司服务器获取导航数据,然后,手动绘制导航路线 |
方案三 | 利用第三方SDK实现导航功能(百度地图) |
?说明
:通常如果需要手动绘制导航路线,都是向Apple服务器发送请求、获取导航信息;此处,只对方案一、方案二做详细介绍,方案三将单独说明。
2、系统自带地图APP导航:
?了解
:采用逆推法
,实现使用Apple系统自带地图进行导航;该方式导航关键在于:[MKMapItem openMapsWithItems:导航路线点 launchOptions:启动参数]
。
2-1、详细步骤:
步骤 | 详细介绍 | 备注 |
---|---|---|
第一步 | 设置导航路线的起点、终点,将目标地址地理编码成CLLPlacemark地标对象,根据CLLPlacemark对象创建MKPlacemark对象,MKPlacemark对象在地图上代表每一个点、再根据MKPlacemark对象创建对应的模型,即:MKMapItem对象 | MKMapItem |
第二步 | 设置起点、终点模型数组,配置打开系统地图APP启动参数 | 启动地图应用前相关准备工作 |
第三步 | 通过MKMapItem的openMapsWithItems:launchOptions: 方法打开系统自带地图进行导航 |
MKMapItem类方法 |
?重要
:通过地理编码获取目标位置的CLPlacemark对象,再根据CLPlacemark对象创建MKPlacemark对象;然后,根据MKPlacemark对象创建对应的数据模型对象MKMapItem对象,最后,将所有的导航路线的数据模型对象包装成数组,通过MKMapItem对象的openMapsWithItems:launchOptions:类方法传递给系统自带地图APP;最终,系统自带地图APP根据传入的数据模型对象完成导航操作。
2-2、补充说明:
- 1、打开系统地图应用时,如果没有设置启动参数,用户进入系统地图之后需要一步一步自主获取导航信息。
- 2、MKPlacemark继承自CLPlacemark,所以,通过CLPlacemark对象可以创建一个MKPlacemark对象。
- 3、通过MKMapItem的mapItemForCurrentLocation类方法可以直接获取当前用户所在位置对应的数据模型,但是,需要请求用户定位授权,因为,牵涉到定位用户当前所在位置。
- 4、使用自带地图APP进行导航的本质:是确定导航路线中要经过的点(CLLPlacemark - - -> MKPlacemark),再将地图上导航所需要用到的地标对象(MKPlacemark)转换成对应的模型(MKMapItem),再将所有的模型通过MKMapItem的openMapsWithItems:launchOptions:类方法传递给系统地图APP,这样就能借助系统地图对指定位置进行导航。
- 5、使用系统地图APP进行导航核心类:MKMapItem、MKPlacemark。
2-3、示例代码:
1. // MARK: - 设置起点、终点
2. // 1. 将当前位置设置为起点模型
3. MKMapItem *beginItem = [MKMapItem mapItemForCurrentLocation];
4.
5. // 2. 设置终点
6. [self.geocoder geocodeAddressString:@"长沙" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
7. // 2.1 获取地标对象
8. CLPlacemark *clPlacemark = [placemarks firstObject];
9.
10. // 2.2 根据CLPlacemark创建MKPlacemark
11. MKPlacemark *endPlacemark = [[MKPlacemark alloc] initWithPlacemark:clPlacemark];
12.
13. // 2.3 创建终点模型
14. MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPlacemark];
15.
16. // MARK: - 使用MKMapItem打开系统地图进行导航
17. // 1. 起点、终点模型数组
18. NSArray *items = @[beginItem, endItem];
19.
20. // 2. 设置启动参数
21. NSDictionary *dict = @{
22. MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
23. MKLaunchOptionsMapTypeKey : @(MKMapTypeStandard),
24. MKLaunchOptionsShowsTrafficKey : @(YES)
25. };
26.
27. // 3. 打开系统地图APP, 进行导航
28. [MKMapItem openMapsWithItems:items launchOptions:dict];
29. }];
3、手动绘制导航路线:
?了解
:想要实现手动绘制导航路线,需要向Apple服务器发送网络请求获取导航路线;需要记住关键对象:导航对象
- - ->MKDirections
。
?重要
:显示在屏幕上的导航路线也是一个覆盖层,在地图上操作覆盖层,其实操作的是覆盖层的数据模型;删除覆盖层:在地图上移除覆盖层数据模型;添加覆盖层:在地图上添加覆盖层数据模型。
3-1、详细步骤:
步骤 | 详细介绍 | 备注 |
---|---|---|
第一步 | 创建导航请求对象(MKDirectionsRequest),在该对象中保存有本次导航的起点模型(MKMapItem)、终点模型(MKMapItem),MKDirectionsRequest对象的source、destination属性专门用来保存起点、终点模型 | 创建请求 |
第二步 | 通过导航请求对象创建导航对象(MKDirections) | 创建导航对象 |
第三步 | 导航对象(MKDirections)调用calculateDirectionsWithCompletionHandler:方法开始向Apple服务器发送请求、获取导航数据信息 | 获取导航数据 |
第四步 | 从Apple服务器获取到导航数据之后开始解析数据(注:导航数据存放在响应体MKDirectionsResponse对象中),从response中便能获取导航路线对象数组、遍历该数组获取导航路线对象(MKRoute)、将导航路线对象中的要显示的渲染图层对应的数据模型添加到mapView中 | 解析导航数据 |
第五步 | 实现返回要显示在mapView上的渲染图层代理方法,在该代理方法中创建渲染图层(MKPolylineRenderer、MKCircleRenderer,具体渲染图层由添加到mapView中的数据模型的类型决定;MKPolyline:折线数据模型,MKCircle:原型数据模型) | 返回渲染图层 |
3-2、详细说明示例图:
?说明
:本质:向Apple服务器发送请求,获取导航数据;获取的导航数据实质是导航路线对象,将导航路线对象中的渲染图层数据模型属性添加到mapView;实现mapView的代理方法,在代理方法中创建渲染图层、并设置相关属性,系统自动将渲染图层添加到mapView上面。
3-3、示例代码:
1.#import "ViewController.h"
2.#import <MapKit/MapKit.h>
3.
4.@interface ViewController () <MKMapViewDelegate>
5./** 地图 */
6.@property (weak, nonatomic) IBOutlet MKMapView *mapView;
7./** 地理编码 */
8.@property (strong, nonatomic) CLGeocoder *geocoder;
9./** 位置管理者对象 */
10.@property (strong, nonatomic) CLLocationManager *mgr;
11.
12.@end
13.
14.@implementation ViewController
15.
16.- (void)viewDidLoad {
17. [super viewDidLoad];
18.
19. // MARK: - 请求授权
20. self.mgr = [CLLocationManager new];
21. if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
22. [self.mgr requestWhenInUseAuthorization];
23. }
24.
25. // MARK: - 设置代理
26. self.mapView.delegate = self;
27.}
28.
29.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
30. // MARK: - 开始导航
31. [self.geocoder geocodeAddressString:@"长沙" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
32. // 0. 地理编码失败, 返回
33. if (placemarks.count == 0 || error) {
34. NSLog(@"地理编码失败");
35. return ;
36. }
37.
38. // MARK: - 创建导航请求对象
39. MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
40.
41. // MARK: - 设置起点、终点模型
42. // 1. 起点模型, 使用该方式获取当前点对应的数据模型 "需要请求用户定位授权"
43. MKMapItem *beginItem = [MKMapItem mapItemForCurrentLocation];
44. request.source = beginItem;
45.
46. // 2. 设置终点
47. CLPlacemark *endCL = placemarks.firstObject;
48. MKPlacemark *endPL = [[MKPlacemark alloc] initWithPlacemark:endCL];
49. MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPL];
50. request.destination = endItem;
51.
52. // 3. 根据MKDirectionsRequest对象, 创建导航对象
53. MKDirections *direction = [[MKDirections alloc] initWithRequest:request];
54.
55. // 4. 计算导航路线
56. [direction calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
57.
58. if (error != nil) {
59. NSLog(@"导航失败");
60. }
61. NSLog(@"%zd", response.routes.count);
62.
63. // 4.1. 遍历导航路线对象数组, MKRoute : 导航路线对象
64. [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
65. // 4.2 将导航路线对象中保存的渲染图层对应的数据模型添加到mapView中
66. [self.mapView addOverlay:obj.polyline];
67. }];
68. }];
69. }];
70.}
71.
72.#pragma mark - <MKMapViewDelegate>
73.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{
74. // 1. 创建折线渲染图层
75. MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
76.
77. // 2. 设置折线渲染图层相关属性
78. renderer.lineWidth = 10; // 折线渲染图层折线线宽
79. renderer.strokeColor = [UIColor greenColor]; // 折线渲染图层折线颜色
80.
81. // 3. 返回折线渲染图层
82. return renderer;
83.}
84.
85.#pragma mark - 懒加载
86.- (CLGeocoder *)geocoder{
87. if (_geocoder == nil) {
88. _geocoder = [[CLGeocoder alloc] init];
89. }
90. return _geocoder;
91.}
92.@end
4、重要类:
4-1、MKDirectionsResponse—响应体对象:
属性名 | 作用 |
---|---|
source | 导航起点位置模型 |
destination | 导航终点位置模型 |
routes | 导航路线对象数组,数组中存放MKRoute对象 |
4-2、MKRoute—导航路线对象:
属性名 | 作用 |
---|---|
name | 导航路线名称 |
advisoryNotices | 导航路线中注意、警告信息 |
distance | 导航路线长度(实际物理距离,单位:m) |
polyline | 导航路线渲染图层几何形状数据模型(即:该数据模型对应的渲染图层的形状为折线,将来往mapView中添加该类型数据模型时,在代理方法中应当创建折线渲染图层返回) |
steps | 多个行走步骤组成的数组(例如“前方路口左转”,“保持直行”等等, MKRouteStep 对象) |
?注意
:MKRoute是一整条路,MKRouteStep是这条长路中的每一节。
4-3、MKRouteStep—导航步骤对象:
属性名 | 作用 |
---|---|
instructions | 步骤说明(例如“前方路口左转”,“保持直行”等) |
transportType | 交通方式(驾车,步行等) |
polyline | 路线对应的在地图上的几何线路数据模型(由很多点组成,可绘制在地图上) |
5、导航渲染图层效果:
5-1、折线渲染图层效果:
示例代码:
1.#import "ViewController.h"
2.#import <MapKit/MapKit.h>
3.
4.@interface ViewController () <MKMapViewDelegate>
5./** 地图 */
6.@property (weak, nonatomic) IBOutlet MKMapView *mapView;
7./** 地理编码 */
8.@property (strong, nonatomic) CLGeocoder *geocoder;
9./** 位置管理者对象 */
10.@property (strong, nonatomic) CLLocationManager *mgr;
11.@end
12.
13.@implementation ViewController
14.
15.- (void)viewDidLoad {
16. [super viewDidLoad];
17.
18. // MARK: - 设置代理
19. self.mapView.delegate = self;
20.
21. // MARK: - 请求授权
22. self.mgr = [[CLLocationManager alloc] init];
23. if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
24. [self.mgr requestWhenInUseAuthorization];
25. }
26.}
27.
28.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
29. // MARK: - 创建导航请求对象
30. MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
31.
32. // 1. 设置起点数据模型
33. MKMapItem *beginItem = [MKMapItem mapItemForCurrentLocation];
34.
35. // 2. 设置终点数据模型
36. [self.geocoder geocodeAddressString:@"耒阳" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
37. // 3. 创建MKPlacemark对象
38. CLPlacemark *endCL = placemarks.firstObject;
39. MKPlacemark *endPL = [[MKPlacemark alloc] initWithPlacemark:endCL];
40.
41. // 4. 创建终点数据模型
42. MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPL];
43.
44. // 5. 设置为请求对象
45. request.source = beginItem;
46. request.destination = endItem;
47.
48. // MARK: - 创建导航对象
49. MKDirections *direcion = [[MKDirections alloc] initWithRequest:request];
50.
51. // 1. 计算导航数据
52. [direcion calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
53. // 2. 防错处理
54. if (placemarks.count == 0 || error) {
55. NSLog(@"请求导航错误");
56. return ;
57. }
58.
59. // 3. 遍历导航路线对象数组, 将折线渲染图层数据模型添加到mapView
60. [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
61. // 4. 将渲染层数据模型添加mapView
62. [self.mapView addOverlay:obj.polyline];
63. }];
64. }];
65. }];
66.}
67.
68.#pragma mark - <MKMapViewDelegate>
69.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{
70. // 1. 创建折线渲染图层
71. MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
72.
73. // 2. 设置相关属性
74. renderer.lineWidth = 10;
75. renderer.strokeColor = [UIColor redColor];
76.
77. // 3. 返回折线渲染图层
78. return renderer;
79.}
80.
81.#pragma mark - 地理编码
82.- (CLGeocoder *)geocoder{
83. if (_geocoder == nil) {
84. _geocoder = [[CLGeocoder alloc] init];
85. }
86. return _geocoder;
87.}
88.@end
效果示例图:
5-2、圆形渲染图层效果:
示例代码:
1.#import "ViewController.h"
2.#import <MapKit/MapKit.h>
3.
4.@interface ViewController () <MKMapViewDelegate>
5./** 地图 */
6.@property (weak, nonatomic) IBOutlet MKMapView *mapView;
7.
8.@end
9.
10.@implementation ViewController
11.
12.- (void)viewDidLoad {
13. [super viewDidLoad];
14.
15. // MARK: - 设置mapView的代理
16. self.mapView.delegate = self;
17.}
18.
19.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
20. // MARK: - 添加圆形渲染图层模型
21. // 1. 圆形渲染图层中心点
22. CLLocationCoordinate2D center = CLLocationCoordinate2DMake(40, 116);
23.
24. // 2. 创建圆形渲染图层数据模型
25. MKCircle *circle = [MKCircle circleWithCenterCoordinate:center radius:1000000];
26.
27. // 3. 添加到mapView
28. [self.mapView addOverlay:circle];
29.}
30.
31.#pragma mark - <MKMapViewDelegate>
32./**
33. 当往mapView中添加数据模型时, 便会调用该方法返回对应的渲染图层
34.
35. @param mapView 地图
36. @param overlay 渲染图层模型
37. @return 渲染图层
38. */
39.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{
40. // 1. 创建圆形渲染图层
41. MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithOverlay:overlay];
42.
43. // 2. 设置属性
44. renderer.fillColor = [UIColor greenColor];
45. renderer.alpha = 0.5;
46.
47. // 3. 返回圆形渲染图层
48. return renderer;
49.}
50.@end
效果示例图:
iOS核心笔记—MapKit框架-导航