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创建MKPlacemark11. 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.[email protected] ViewController () <MKMapViewDelegate>5./** 地图 */[email protected] (weak, nonatomic) IBOutlet MKMapView *mapView;7./** 地理编码 */[email protected] (strong, nonatomic) CLGeocoder *geocoder;9./** 位置管理者对象 */[email protected] (strong, nonatomic) CLLocationManager *mgr;11.[email protected]13.[email protected] ViewController15.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.}[email protected]
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.[email protected] ViewController () <MKMapViewDelegate>5./** 地图 */[email protected] (weak, nonatomic) IBOutlet MKMapView *mapView;7./** 地理编码 */[email protected] (strong, nonatomic) CLGeocoder *geocoder;9./** 位置管理者对象 */[email protected] (strong, nonatomic) CLLocationManager *mgr;[email protected]12.[email protected] ViewController14.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. 遍历导航路线对象数组, 将折线渲染图层数据模型添加到mapView60. [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {61. // 4. 将渲染层数据模型添加mapView62. [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.}[email protected]
效果示例图:
5-2、圆形渲染图层效果:
示例代码:
1.#import "ViewController.h"2.#import <MapKit/MapKit.h>3.[email protected] ViewController () <MKMapViewDelegate>5./** 地图 */[email protected] (weak, nonatomic) IBOutlet MKMapView *mapView;7.[email protected]9.[email protected] ViewController11.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. 添加到mapView28. [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.}[email protected]
效果示例图: