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 1.#import "ViewController.h" 2 2.#import <MapKit/MapKit.h> 3 3. 4 4.@interface ViewController () <MKMapViewDelegate> 5 5./** 地图 */ 6 6.@property (weak, nonatomic) IBOutlet MKMapView *mapView; 7 7./** 地理编码 */ 8 8.@property (strong, nonatomic) CLGeocoder *geocoder; 9 9./** 位置管理者对象 */ 10 10.@property (strong, nonatomic) CLLocationManager *mgr; 11 11. 12 12.@end 13 13. 14 14.@implementation ViewController 15 15. 16 16.- (void)viewDidLoad { 17 17. [super viewDidLoad]; 18 18. 19 19. // MARK: - 请求授权 20 20. self.mgr = [CLLocationManager new]; 21 21. if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) { 22 22. [self.mgr requestWhenInUseAuthorization]; 23 23. } 24 24. 25 25. // MARK: - 设置代理 26 26. self.mapView.delegate = self; 27 27.} 28 28. 29 29.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 30 30. // MARK: - 开始导航 31 31. [self.geocoder geocodeAddressString:@"长沙" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { 32 32. // 0. 地理编码失败, 返回 33 33. if (placemarks.count == 0 || error) { 34 34. NSLog(@"地理编码失败"); 35 35. return ; 36 36. } 37 37. 38 38. // MARK: - 创建导航请求对象 39 39. MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init]; 40 40. 41 41. // MARK: - 设置起点、终点模型 42 42. // 1. 起点模型, 使用该方式获取当前点对应的数据模型 "需要请求用户定位授权" 43 43. MKMapItem *beginItem = [MKMapItem mapItemForCurrentLocation]; 44 44. request.source = beginItem; 45 45. 46 46. // 2. 设置终点 47 47. CLPlacemark *endCL = placemarks.firstObject; 48 48. MKPlacemark *endPL = [[MKPlacemark alloc] initWithPlacemark:endCL]; 49 49. MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPL]; 50 50. request.destination = endItem; 51 51. 52 52. // 3. 根据MKDirectionsRequest对象, 创建导航对象 53 53. MKDirections *direction = [[MKDirections alloc] initWithRequest:request]; 54 54. 55 55. // 4. 计算导航路线 56 56. [direction calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) { 57 57. 58 58. if (error != nil) { 59 59. NSLog(@"导航失败"); 60 60. } 61 61. NSLog(@"%zd", response.routes.count); 62 62. 63 63. // 4.1. 遍历导航路线对象数组, MKRoute : 导航路线对象 64 64. [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 65 65. // 4.2 将导航路线对象中保存的渲染图层对应的数据模型添加到mapView中 66 66. [self.mapView addOverlay:obj.polyline]; 67 67. }]; 68 68. }]; 69 69. }]; 70 70.} 71 71. 72 72.#pragma mark - <MKMapViewDelegate> 73 73.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{ 74 74. // 1. 创建折线渲染图层 75 75. MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; 76 76. 77 77. // 2. 设置折线渲染图层相关属性 78 78. renderer.lineWidth = 10; // 折线渲染图层折线线宽 79 79. renderer.strokeColor = [UIColor greenColor]; // 折线渲染图层折线颜色 80 80. 81 81. // 3. 返回折线渲染图层 82 82. return renderer; 83 83.} 84 84. 85 85.#pragma mark - 懒加载 86 86.- (CLGeocoder *)geocoder{ 87 87. if (_geocoder == nil) { 88 88. _geocoder = [[CLGeocoder alloc] init]; 89 89. } 90 90. return _geocoder; 91 91.} 92 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 1.#import "ViewController.h" 2 2.#import <MapKit/MapKit.h> 3 3. 4 4.@interface ViewController () <MKMapViewDelegate> 5 5./** 地图 */ 6 6.@property (weak, nonatomic) IBOutlet MKMapView *mapView; 7 7./** 地理编码 */ 8 8.@property (strong, nonatomic) CLGeocoder *geocoder; 9 9./** 位置管理者对象 */ 10 10.@property (strong, nonatomic) CLLocationManager *mgr; 11 11.@end 12 12. 13 13.@implementation ViewController 14 14. 15 15.- (void)viewDidLoad { 16 16. [super viewDidLoad]; 17 17. 18 18. // MARK: - 设置代理 19 19. self.mapView.delegate = self; 20 20. 21 21. // MARK: - 请求授权 22 22. self.mgr = [[CLLocationManager alloc] init]; 23 23. if ([self.mgr respondsToSelector:@selector(requestWhenInUseAuthorization)]) { 24 24. [self.mgr requestWhenInUseAuthorization]; 25 25. } 26 26.} 27 27. 28 28.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 29 29. // MARK: - 创建导航请求对象 30 30. MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init]; 31 31. 32 32. // 1. 设置起点数据模型 33 33. MKMapItem *beginItem = [MKMapItem mapItemForCurrentLocation]; 34 34. 35 35. // 2. 设置终点数据模型 36 36. [self.geocoder geocodeAddressString:@"耒阳" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { 37 37. // 3. 创建MKPlacemark对象 38 38. CLPlacemark *endCL = placemarks.firstObject; 39 39. MKPlacemark *endPL = [[MKPlacemark alloc] initWithPlacemark:endCL]; 40 40. 41 41. // 4. 创建终点数据模型 42 42. MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endPL]; 43 43. 44 44. // 5. 设置为请求对象 45 45. request.source = beginItem; 46 46. request.destination = endItem; 47 47. 48 48. // MARK: - 创建导航对象 49 49. MKDirections *direcion = [[MKDirections alloc] initWithRequest:request]; 50 50. 51 51. // 1. 计算导航数据 52 52. [direcion calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) { 53 53. // 2. 防错处理 54 54. if (placemarks.count == 0 || error) { 55 55. NSLog(@"请求导航错误"); 56 56. return ; 57 57. } 58 58. 59 59. // 3. 遍历导航路线对象数组, 将折线渲染图层数据模型添加到mapView 60 60. [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 61 61. // 4. 将渲染层数据模型添加mapView 62 62. [self.mapView addOverlay:obj.polyline]; 63 63. }]; 64 64. }]; 65 65. }]; 66 66.} 67 67. 68 68.#pragma mark - <MKMapViewDelegate> 69 69.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{ 70 70. // 1. 创建折线渲染图层 71 71. MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; 72 72. 73 73. // 2. 设置相关属性 74 74. renderer.lineWidth = 10; 75 75. renderer.strokeColor = [UIColor redColor]; 76 76. 77 77. // 3. 返回折线渲染图层 78 78. return renderer; 79 79.} 80 80. 81 81.#pragma mark - 地理编码 82 82.- (CLGeocoder *)geocoder{ 83 83. if (_geocoder == nil) { 84 84. _geocoder = [[CLGeocoder alloc] init]; 85 85. } 86 86. return _geocoder; 87 87.} 88 88.@end
效果示例图:
5-2、圆形渲染图层效果:
示例代码:
1 1.#import "ViewController.h" 2 2.#import <MapKit/MapKit.h> 3 3. 4 4.@interface ViewController () <MKMapViewDelegate> 5 5./** 地图 */ 6 6.@property (weak, nonatomic) IBOutlet MKMapView *mapView; 7 7. 8 8.@end 9 9. 10 10.@implementation ViewController 11 11. 12 12.- (void)viewDidLoad { 13 13. [super viewDidLoad]; 14 14. 15 15. // MARK: - 设置mapView的代理 16 16. self.mapView.delegate = self; 17 17.} 18 18. 19 19.- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 20 20. // MARK: - 添加圆形渲染图层模型 21 21. // 1. 圆形渲染图层中心点 22 22. CLLocationCoordinate2D center = CLLocationCoordinate2DMake(40, 116); 23 23. 24 24. // 2. 创建圆形渲染图层数据模型 25 25. MKCircle *circle = [MKCircle circleWithCenterCoordinate:center radius:1000000]; 26 26. 27 27. // 3. 添加到mapView 28 28. [self.mapView addOverlay:circle]; 29 29.} 30 30. 31 31.#pragma mark - <MKMapViewDelegate> 32 32./** 33 33. 当往mapView中添加数据模型时, 便会调用该方法返回对应的渲染图层 34 34. 35 35. @param mapView 地图 36 36. @param overlay 渲染图层模型 37 37. @return 渲染图层 38 38. */ 39 39.- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay{ 40 40. // 1. 创建圆形渲染图层 41 41. MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithOverlay:overlay]; 42 42. 43 43. // 2. 设置属性 44 44. renderer.fillColor = [UIColor greenColor]; 45 45. renderer.alpha = 0.5; 46 46. 47 47. // 3. 返回圆形渲染图层 48 48. return renderer; 49 49.} 50 50.@end
效果示例图: