要想让MapView画路径,必然要传给mapView某些东西,没错,相似Annotation(大头针),添加路径也有相似的操做函数和相应传入CLLocation返回路径的协议方法。数组
1.搭建界面函数
拖一个全屏的MapView,连线到controller,以下ui
1 @property (weak, nonatomic) IBOutlet MKMapView *mapView;
2. 建立一个编码器(地理编码,不要误解)编码
1 @property (nonatomic, strong) CLGeocoder *geocoder;
并让它成为懒加载对象:atom
1 - (CLGeocoder *)geocoder 2 { 3 if (!_geocoder) { 4 self.geocoder = [[CLGeocoder alloc] init]; 5 } 6 return _geocoder; 7 }
上面这些不走都是无脑完成的,写的多了都轻车熟路。下面才是重头戏。spa
3. 开始以前先拿到起始点和终点的地理信息再说代理
(1) 在这以前咱们是否是要设置mapView的代理呢code
遵照协议对象
1 @interface ViewController () <MKMapViewDelegate>
设置代理:blog
1 self.mapView.delegate = self;
(2) 开始干活
1 NSString *address1 = @"北京"; 2 NSString *address2 = @"上海"; 3 4 [self.geocoder geocodeAddressString:address1 completionHandler:^(NSArray *placemarks, NSError *error) { 5 if (error) return; 6 7 CLPlacemark *fromPm = [placemarks firstObject]; 8 9 [self.geocoder geocodeAddressString:address2 completionHandler:^(NSArray *placemarks, NSError *error) { 10 if (error) return; 11 12 CLPlacemark *toPm = [placemarks firstObject]; 13 14 [self addLineFrom:fromPm to:toPm]; 15 }]; 16 }];
有心的同志可能观察到:怎么两个反地理编码是嵌套的。呐,这里是你要注意的,在一个controller中只能同时存在一个地理编码信息。若是反编码完起始点就跳出block,那么当反编码完第二个的时候,第一个就被销毁了。而嵌套block则不会。这里你能够理解为block里面的信息还不归属controller管理。
咱们反地理编码获得的老是一系列的符合结果的地理信息数组,可是只有最好的那个才是咱们须要的,因此老是取出数组中的第一个object。地理信息的类型是CLPlacemark类型,这个类型在添加大头针的时候确定不会陌生。它就是封装了地理坐标CLLocationCoordinate的一个类型。
咱们经过反地理编码的获得了两个地理信息,那接下来就开始划线吧。方法
addLineFrom:fromPm to:toPm
是咱们本身建立的,这样防止一个方法里面放置太多的代码,也实现代码的聚合性(一段代码只实现一个功能)。
4. 实现addLineFrom:(CLPlacemark *)from to:(CLPlacemark *)to
(1) 虽然咱们设置了开始和结束,可是咱们并无告诉mapView究竟是从"北京"到"上海",仍是"上海"到"北京",咱们如今设置的信息都只是咱们单方面的一厢情愿的。
1 // 方向请求 2 MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init]; 3 // 设置起点 4 MKPlacemark *sourcePm = [[MKPlacemark alloc] initWithPlacemark:fromPm]; 5 request.source = [[MKMapItem alloc] initWithPlacemark:sourcePm]; 6 7 // 设置终点 8 MKPlacemark *destinationPm = [[MKPlacemark alloc] initWithPlacemark:toPm]; 9 request.destination = [[MKMapItem alloc] initWithPlacemark:destinationPm];
传进来的CLPlacemark对象又一次被封装到MKPlacemark中了。这是能够理解的,毕竟MapView和CLLocation仍是有一点区别的。
这还没完,到这里咱们只是设置了请求(request),尚未请求,下面咱们请求设置方向
1 // 方向对象 2 MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
方向,和两个地点都有了,至少咱们认为不缺乏什么东西了,告诉你方向,从哪里出发,到那里出发,你还不知道怎么走,那你就去死吧。或许你担忧可能如今的数据还要进一步封装,可是你并不知道怎么封装,那么我告诉你,到这里已经不须要封装了,咱们获得了mapView能识别的数据类型。
(2) 那下面咱们开始添加咯
前面咱们说过,相似添加大头针的addAnnotation:,这里有一个addOverlay:,只要咱们点用这个就能添加路线了。可是添加了显示仍是一回事(就向添加普通view时没有设置frame同样,添加了可是没有显示。可是这里缺不是frame的问题,后面会介绍的)。
从咱们如今的信息得到路线,调用addOverlay:
1 [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) { 2 // NSLog(@"总共%lu条路线", [response.routes count]); 3 4 // code 5 6 }];
咱们调用了MKDirections的方法,你问我为何是这个?这他妈就是这么用的哪有那么多为何。
这个方法会帮你计算路线,完事以后还会回调一个block,而获得的路线和其余信息都封装在response中让你操做,同事还烦会给你了错误信息。
那咱们就不客气了,解析出路线(路线在iOS中是MKRoute对象)
1 for (MKRoute *route in response.routes) { 2 // 添加路线遮盖 3 [self.mapView addOverlay:route.polyline]; 4 }
奥,这里叫路线遮盖,可能你从addOverlay就开始疑问了,为何不是addRoute?能够这么理解,你添加的路线是盖在mapView上面的,而不是mapView自带的属性。
ok,咱们暴力添加了全部获得的路线遮盖,可是运行一下你就知道,并无显示路线,为何呢,咱们的逻辑到如今都都没有错,难道是哪里错了我不知道?实际上是没有错的,是还没完成。
(3) 实现代理方法
须要实现代理方法才能显示。
还记得添加大头针的时候须要实现一个代理方法返回大头针view吗,这里也是
1 #pragma mark - MKMapViewDelegate 2 - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay 3 { 4 MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; 5 renderer.strokeColor = [UIColor redColor]; 6 return renderer; 7 }
就是这个逼,每次调用addOverlay时,都会调用一次这个代理方法。
这个方法传入MapView和录像遮盖overlay,返回MKOverlayRender类型,而MKOverlayRender是第一次出现,是什么鬼?看来得建立MKOverlayRender这个对象并返回他了。
代码中你就看出了,咱们并无用MKOverlayRender这个类型,而是用了MKPolylineRender(MKOverlayRender的子类)。由于MKOverlayRender连个属性都没有。试想一下你添加的路径没有线宽和颜色,那你他妈看得见吗。因此咱们使用MKPolylineRender子类。他有一系列的属性(按住command,左键点击MKPolylineRender便可查看)能够设置,根据喜爱本身发挥吧。
运行你就发现所言非虚了。
5. 添加大头针
这里纯属拓展
在- (void)addLineFrom:(CLPlacemark *)fromPm to:(CLPlacemark *)toPm方法实现开始时添加代码
1 // 1.添加2个大头针 2 LSAnnotation *fromAnno = [[LsAnnotation alloc] init]; 3 fromAnno.coordinate = fromPm.location.coordinate; 4 fromAnno.title = fromPm.name; 5 [self.mapView addAnnotation:fromAnno]; 6 7 LsAnnotation *toAnno = [[LsAnnotation alloc] init]; 8 toAnno.coordinate = toPm.location.coordinate; 9 toAnno.title = toPm.name; 10 [self.mapView addAnnotation:toAnno];
这里就是纯添加两个大头针,利用传进来的两个地点信息。LsAnnotation时本身建立的实现了<MKAnnotation>协议的大头针,并添加了几个属性,添加出来的就是一个红色的大头针,你想要其余样式请移步到其余教程。
这里给出.h文件,.m文件没有任何实现,因此不给了
1 #import <Foundation/Foundation.h> 2 #import <MapKit/MapKit.h> 3 @interface LsAnnotation : NSObject <MKAnnotation> 4 @property (nonatomic) CLLocationCoordinate2D coordinate;//required 5 @property (nonatomic, copy) NSString *title; 6 @property (nonatomic, copy) NSString *subtitle; 7 8 @end
ok,结束了。
给出controller的完整代码
1 // 2 // LsViewController.m 3 // 07-导航画线 4 // 5 // 6 7 #import "LsViewController.h" 8 #import "LsAnnotation.h" 9 #import <MapKit/MapKit.h> 10 #import <CoreLocation/CoreLocation.h> 11 12 @interface MJViewController () <MKMapViewDelegate> 13 @property (weak, nonatomic) IBOutlet MKMapView *mapView; 14 @property (nonatomic, strong) CLGeocoder *geocoder; 15 @end 16 17 @implementation MJViewController 18 19 - (CLGeocoder *)geocoder 20 { 21 if (!_geocoder) { 22 self.geocoder = [[CLGeocoder alloc] init]; 23 } 24 return _geocoder; 25 } 26 27 - (void)viewDidLoad 28 { 29 [super viewDidLoad]; 30 31 self.mapView.delegate = self; 32 33 NSString *address1 = @"北京"; 34 NSString *address2 = @"广州"; 35 36 [self.geocoder geocodeAddressString:address1 completionHandler:^(NSArray *placemarks, NSError *error) { 37 if (error) return; 38 39 CLPlacemark *fromPm = [placemarks firstObject]; 40 41 [self.geocoder geocodeAddressString:address2 completionHandler:^(NSArray *placemarks, NSError *error) { 42 if (error) return; 43 44 CLPlacemark *toPm = [placemarks firstObject]; 45 46 [self addLineFrom:fromPm to:toPm]; 47 }]; 48 }]; 49 50 51 } 52 53 /** 54 * 添加导航的线路 55 * 56 * @param fromPm 起始位置 57 * @param toPm 结束位置 58 */ 59 - (void)addLineFrom:(CLPlacemark *)fromPm to:(CLPlacemark *)toPm 60 { 61 // 1.添加2个大头针 62 LsAnnotation *fromAnno = [[LsAnnotation alloc] init]; 63 fromAnno.coordinate = fromPm.location.coordinate; 64 fromAnno.title = fromPm.name; 65 [self.mapView addAnnotation:fromAnno]; 66 67 LsAnnotation *toAnno = [[LsAnnotation alloc] init]; 68 toAnno.coordinate = toPm.location.coordinate; 69 toAnno.title = toPm.name; 70 [self.mapView addAnnotation:toAnno]; 71 72 // 2.查找路线 73 74 // 方向请求 75 MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init]; 76 // 设置起点 77 MKPlacemark *sourcePm = [[MKPlacemark alloc] initWithPlacemark:fromPm]; 78 request.source = [[MKMapItem alloc] initWithPlacemark:sourcePm]; 79 80 // 设置终点 81 MKPlacemark *destinationPm = [[MKPlacemark alloc] initWithPlacemark:toPm]; 82 request.destination = [[MKMapItem alloc] initWithPlacemark:destinationPm]; 83 84 // 方向对象 85 MKDirections *directions = [[MKDirections alloc] initWithRequest:request]; 86 87 // 计算路线 88 [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) { 89 // NSLog(@"总共%lu条路线", [response.routes count]); 90 91 // 遍历全部的路线 92 for (MKRoute *route in response.routes) { 93 // 添加路线遮盖 94 [self.mapView addOverlay:route.polyline]; 95 } 96 }]; 97 } 98 99 #pragma mark - MKMapViewDelegate 100 - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay 101 { 102 MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; 103 renderer.strokeColor = [UIColor redColor]; 104 return renderer; 105 } 106 @end