• 【iOS】7.4 定位服务->3.2 地图框架MapKit 功能2:路线规划(导航)


    本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正。


    本文相关目录:
    ================== 所属文集:【iOS】07 设备工具 ==================
    7.4 定位服务->1.0 简介
    7.4 定位服务->2.1.1 定位 - 官方框架CoreLocation: 请求用户授权
    7.4 定位服务->2.1.2 定位 - 官方框架CoreLocation: CLLocationManager位置管理器
    7.4 定位服务->2.1.3.1 定位 - 官方框架CoreLocation 功能1:地理定位
    7.4 定位服务->2.1.3.2 定位 - 官方框架CoreLocation 功能2:地理编码和反地理编码
    7.4 定位服务->2.1.3.3 定位 - 官方框架CoreLocation 功能3:区域监听
    7.4 定位服务->2.1.4 定位 - 官方框架CoreLocation 案例:指南针效果
    7.4 定位服务->2.2 定位 - locationManager框架
    7.4 定位服务->3.1 地图框架MapKit 功能1:地图展示
    7.4 定位服务->3.2 地图框架MapKit 功能2:路线规划(导航)
    7.4 定位服务->3.3 地图框架MapKit 功能3:3D视图
    7.4 定位服务->3.4 地图框架MapKit 功能4:地图截图
    7.4 定位服务->3.5 地图框架MapKit 功能5:POI检索
    ================== 所属文集:【iOS】07 设备工具 ==================


    地图框架 - MapKit目录:

    本文目录:


    1.0 概念


    2.0 导航的3种实现方案

    2.1 方案1:使用系统APP导航

    代码16:使用系统APP导航 Demo

    编译环境:Xcode 8.0
    模拟器版本:iOS 10
    Swift版本:3.0

    【OC 语言】
    #import "ViewController.h"
    #import <CoreLocation/CoreLocation.h>
    #import <MapKit/MapKit.h>
    
    @interface ViewController ()
    @property (nonatomic, strong) CLGeocoder *geoCoder;
    @end
    
    @implementation ViewController
    
    -(CLGeocoder *)geoCoder{
        if (!_geoCoder) {
            _geoCoder = [[CLGeocoder alloc] init];
        }
        return _geoCoder;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        // 导航包括:起点和终点  数据由苹果处理
        [self.geoCoder geocodeAddressString:@"广州" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
            
            // 1. 拿到广州地标对象
            CLPlacemark *gzP = [placemarks firstObject];
            
            [self.geoCoder geocodeAddressString:@"上海" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
                
                // 2. 拿到上海地标对象
                CLPlacemark *shP = [placemarks firstObject];
                
                // 3. 调用开始导航的方法(从广州到上海)
                [self directionsWithBeginPlackmark:gzP andEndPlacemark:shP];
            }];
        }];
    }
    
    #pragma mark - 开始导航
    // 根据两个地标,向苹果服务器请求对应的行走路线信息
    - (void)directionsWithBeginPlackmark:(CLPlacemark *)startPLCL andEndPlacemark:(CLPlacemark *)endPLCL{
        
        // 创建起点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
        MKPlacemark *startplMK = [[MKPlacemark alloc] initWithPlacemark:startPLCL];
        MKMapItem *startItem = [[MKMapItem alloc] initWithPlacemark:startplMK];
        
        // 创建终点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
        MKPlacemark *endplMK = [[MKPlacemark alloc] initWithPlacemark:endPLCL];
        MKMapItem *endItem = [[MKMapItem alloc] initWithPlacemark:endplMK];
        
        // 设置导航地图启动项参数字典
        NSDictionary *launchDic = @{
              // 导航模式:驾驶
              MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
              // 地图样式:标准样式
              MKLaunchOptionsMapTypeKey : @(MKMapTypeStandard),
              // 显示交通:显示
              MKLaunchOptionsShowsTrafficKey : @(YES),
        };
        
        // 根据 MKMapItem 的起点和终点组成数组, 通过导航地图启动项参数字典, 调用系统的地图APP进行导航
        [MKMapItem openMapsWithItems:@[startItem, endItem] launchOptions:launchDic];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    
    【Swift】
    import UIKit
    import MapKit
    
    class ViewController: UIViewController {
    
        // MARK: - 懒加载
        lazy var geoCoder: CLGeocoder = {
            return CLGeocoder()
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
        }
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            // MARK: - 地理编码(导航包括:起点和终点  数据由苹果处理)
            geoCoder.geocodeAddressString("广州") { (pls: [CLPlacemark]?, error) -> Void in
                // 1. 拿到广州地标对象
                let gzPL = pls?.first
                
                self.geoCoder.geocodeAddressString("上海") { (pls: [CLPlacemark]?, error) -> Void in
                    // 2. 拿到上海地标对象
                    let shPL = pls?.first
                    
                    // 3. 调用开始导航的方法(从广州到上海)
                    self.beginNav(gzPL!, endPLCL: shPL!)
                }
            }
        }
        
        //MARK: - 开始导航
        func beginNav(_ startPLCL: CLPlacemark, endPLCL: CLPlacemark) {
            
            // 获取起点
            let startplMK: MKPlacemark = MKPlacemark(placemark: startPLCL)
            let startItem: MKMapItem = MKMapItem(placemark: startplMK)
            
            // 获取终点
            let endplMK: MKPlacemark = MKPlacemark(placemark: endPLCL)
            let endItem: MKMapItem = MKMapItem(placemark: endplMK)
            
            // 设置起点和终点
            let mapItems: [MKMapItem] = [startItem, endItem]
            
            // 设置导航地图启动项参数字典
            let dic: [String : AnyObject] = [
                // 导航模式:驾驶
                MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving as AnyObject,
                // 地图样式:标准样式
                MKLaunchOptionsMapTypeKey: MKMapType.standard.rawValue as AnyObject,
                // 显示交通:显示
                MKLaunchOptionsShowsTrafficKey: true as AnyObject
            ]
            
            // 根据 MKMapItem 的起点和终点组成数组, 通过导航地图启动项参数字典, 调用系统的地图APP进行导航
            MKMapItem.openMaps(with: mapItems, launchOptions: dic)
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }
    
    

    运行效果:


    2.2 使用手动画线导航




    步骤2的方法:

    步骤4的代理方法:


    代码17:使用手动画线导航 Demo

    编译环境:Xcode 8.0
    模拟器版本:iOS 10
    Swift版本:3.0

    【OC 语言】
    
    #import "ViewController.h"
    #import <CoreLocation/CoreLocation.h>
    #import <MapKit/MapKit.h>
    
    @interface ViewController ()<MKMapViewDelegate>
    @property (weak, nonatomic) IBOutlet MKMapView *mapView;
    @property (nonatomic, strong) CLGeocoder *geoCoder;
    @end
    
    @implementation ViewController
    
    -(CLGeocoder *)geoCoder{
        if (!_geoCoder) {
            _geoCoder = [[CLGeocoder alloc] init];
        }
        return _geoCoder;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.mapView.delegate = self;
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        // 导航包括:起点和终点  数据由苹果处理
        [self.geoCoder geocodeAddressString:@"广州" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
            
            // 1. 拿到广州地标对象
            CLPlacemark *gzP = [placemarks firstObject];
            // 1.2 创建圆形的覆盖层数据模型
            MKCircle *circle1 = [MKCircle circleWithCenterCoordinate:gzP.location.coordinate
                                                              radius:100000];
            // 1.3 添加覆盖层数据模型到地图上
            [self.mapView addOverlay:circle1];
            
            [self.geoCoder geocodeAddressString:@"上海" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
                
                // 2. 拿到上海地标对象
                CLPlacemark *shP = [placemarks firstObject];
                // 2.2 创建圆形的覆盖层数据模型
                MKCircle *circle2 = [MKCircle circleWithCenterCoordinate:shP.location.coordinate
                                                                  radius:100000];
                // 2.3 添加覆盖层数据模型到地图上
                [self.mapView addOverlay:circle2];
                
                // 3. 调用开始导航的方法(从广州到上海)
                [self directionsWithBeginPlackmark:gzP andEndPlacemark:shP];
            }];
        }];
    }
    
    #pragma mark - 开始导航
    // ① 根据两个地标,发送网络请求给苹果服务器获取导航数据,请求对应的行走路线信息
    - (void)directionsWithBeginPlackmark:(CLPlacemark *)startPLCL andEndPlacemark:(CLPlacemark *)endPLCL{
        
        // 创建请求导航路线数据信息
        MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
        
        // 创建起点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
        MKPlacemark *startplMK = [[MKPlacemark alloc] initWithPlacemark:startPLCL];
        request.source = [[MKMapItem alloc] initWithPlacemark:startplMK];
        
        // 创建终点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
        MKPlacemark *endplMK = [[MKPlacemark alloc] initWithPlacemark:endPLCL];
        request.destination = [[MKMapItem alloc] initWithPlacemark:endplMK];
        
        // 1. 创建导航对象,根据请求,获取实际路线信息
        MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
        
        // 2. 调用方法, 开始发送请求,计算路线信息
        [directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
            
            // ② 解析导航数据
            // block遍历所有的路线方案routes (MKRoute对象)
            [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                
                NSLog(@"%@",obj.advisoryNotices);
                NSLog(@"%@,%f,%f",obj.name,obj.distance,obj.expectedTravelTime);
                
                // 添加覆盖层数据模型,路线对应的几何线路模型(由很多点组成)
                // 当我们添加一个覆盖层数据模型时, 系统绘自动查找对应的代理方法, 找到对应的覆盖层"视图"
                [self.mapView addOverlay:obj.polyline];  // 添加折线
                
                // block 遍历每一种路线的每一个步骤(MKRouteStep对象)
                [obj.steps enumerateObjectsUsingBlock:^(MKRouteStep * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    NSLog(@"%@",obj.instructions);// 打印步骤说明
                }];
            }];
           
    // 拓展
    #pragma mark - block遍历
            [response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                
                NSLog(@"1打印对象的属性:%@,2索引:%lu",obj.name,(unsigned long)idx);
                
                // 实现在某个遍历某个索引处停止
                if (idx == 0) {
                    NSLog(@"block停止");
                    *stop = YES;
                }
            }];
            
    #pragma mark - for in遍历   
            // 不知道遍历的对象的情况
            for (id obj in response.routes) {
                NSLog(@"3%@",[obj name]);
            }
            
            // 已知遍历的对象的情况
            for (MKRoute * _Nonnull obj in response.routes) {
                NSLog(@"3%@",obj.name);
            }
            
      
        }];
    }
    
    #pragma mark - MKMapViewDelegate
    // ③ 添加导航路线到地图
    // 当添加一个覆盖层数据模型到地图上时, 地图会调用这个方法, 查找对应的覆盖层"视图"(渲染图层)
    // 参数1(mapView):地图    参数2(overlay):覆盖层"数据模型"   returns: 覆盖层视图
    - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
        
        // 折线覆盖层
        if ([overlay isKindOfClass:[MKPolyline class]]) {
            // 创建折线渲染对象 (不同的覆盖层数据模型, 对应不同的覆盖层视图来显示)
            MKPolylineRenderer *lineRenderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
            
            lineRenderer.lineWidth = 6;                     // 设置线宽
            lineRenderer.strokeColor = [UIColor redColor];  // 设置线颜色
            
            return lineRenderer;
        }
        
        // 圆形覆盖层
        if ([overlay isKindOfClass:[MKCircle class]]) {
            
            MKCircleRenderer *circleRender = [[MKCircleRenderer alloc] initWithOverlay:overlay];
            
            circleRender.fillColor = [UIColor blackColor];  // 设置填充颜色
            circleRender.alpha = 0.6;                       // 设置透明色
            
            return circleRender;
        }
        return nil;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    

    【Swift 语言】

    
    import UIKit
    import MapKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var mapView: MKMapView!
        
        // MARK: - 懒加载
        lazy var geoCoder: CLGeocoder = {
            return CLGeocoder()
        }()
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            // MARK: - 地理编码
            geoCoder.geocodeAddressString("广州") { (pls: [CLPlacemark]?, error) -> Void in
                // 1. 拿到广州地标对象
                let gzPL = pls?.first
                // 1.2 创建圆形的覆盖层数据模型
                let circle1 = MKCircle(center: (gzPL?.location?.coordinate)!, radius: 100000)
                // 1.3 添加覆盖层数据模型到地图上
                self.mapView.add(circle1)
                
                self.geoCoder.geocodeAddressString("上海") { (pls: [CLPlacemark]?, error) -> Void in
                    // 2. 拿到上海地标对象
                    let shPL = pls?.first
                    // 2.2 创建圆形的覆盖层数据模型
                    let circle2 = MKCircle(center: (shPL?.location?.coordinate)!, radius: 100000)
                    // 2.3 添加覆盖层数据模型到地图上
                    self.mapView.add(circle2)
                    
                    // 3. 调用获取导航线路数据信息的方法
                    self.getRouteMessage(gzPL!, endCLPL: shPL!)
                }
            }
        }
        
        // MARK: - ① 根据两个地标,发送网络请求给苹果服务器获取导航数据,请求对应的行走路线信息
        func getRouteMessage(_ startCLPL: CLPlacemark, endCLPL: CLPlacemark) {
            
            // 创建请求导航路线数据信息
            let request: MKDirectionsRequest = MKDirectionsRequest()
            
            // 创建起点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
            let sourceMKPL: MKPlacemark = MKPlacemark(placemark: startCLPL)
            request.source = MKMapItem(placemark: sourceMKPL)
            
            // 创建终点:根据 CLPlacemark 地标对象创建 MKPlacemark 地标对象
            let endMKPL: MKPlacemark = MKPlacemark(placemark: endCLPL)
            request.destination = MKMapItem(placemark: endMKPL)
            
            // 1. 创建导航对象,根据请求,获取实际路线信息
            let directions: MKDirections = MKDirections(request: request)
            
            // 2. 调用方法, 开始发送请求,计算路线信息
            directions.calculate { (response:MKDirectionsResponse?, error:Error?) in
                
                if error == nil {
                    print(response)
                    
                    // MARK: - ② 解析导航数据
                    // 遍历 routes (MKRoute对象):因为有多种路线
                    for route in (response?.routes)! {
                        print(route.advisoryNotices)
                        print(route.name, route.distance, route.expectedTravelTime)
                        
                        // 添加覆盖层数据模型,路线对应的几何线路模型(由很多点组成)
                        // 当我们添加一个覆盖层数据模型时, 系统绘自动查找对应的代理方法, 找到对应的覆盖层"视图"
                        self.mapView.add(route.polyline) // 添加折线
                        
                        // 遍历每一种路线的每一个步骤(MKRouteStep对象)
                        for step in route.steps {
                            print(step.instructions) // 打印步骤说明
                        }
                    }
                }
            }
        }
    }
    
    extension ViewController: MKMapViewDelegate {
        // MARK: - ③ 添加导航路线到地图
        // MARK: - 当添加一个覆盖层数据模型到地图上时, 地图会调用这个方法, 查找对应的覆盖层"视图"(渲染图层)
        // 参数1(mapView):地图    参数2(overlay):覆盖层"数据模型"   returns: 覆盖层视图
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            
            var resultRender = MKOverlayRenderer()
            
            // 折线覆盖层
            if overlay.isKind(of: MKPolyline.self) {
                
                // 创建折线渲染对象 (不同的覆盖层数据模型, 对应不同的覆盖层视图来显示)
                let render: MKPolylineRenderer = MKPolylineRenderer(overlay: overlay)
                
                render.lineWidth = 6                // 设置线宽
                render.strokeColor = UIColor.red    // 设置颜色
                
                resultRender = render
            }
            
            // 圆形覆盖层
            if overlay.isKind(of: MKCircle.self) {
                
                let circleRender: MKCircleRenderer = MKCircleRenderer(overlay: overlay)
                
                circleRender.fillColor = UIColor.black // 设置填充颜色
                circleRender.alpha = 0.5               // 设置透明色
                
                resultRender = circleRender
            }
            return resultRender
        }
    }
    

    运行效果:


    方案3:第三方SDK(百度地图)集成实现导航

    疯狂整理中。。。。。!!!!!!



    本文源码 Demo 详见 Github
    https://github.com/shorfng/iOS_7.0_Device-Tools


    作者:蓝田(Loto)
    【作品发布平台】

    简书
    博客园
    Gitbook(如果觉得文章太长,请阅读此平台发布的文章)

    【代码托管平台】

    Github

    【如有疑问,请通过以下方式交流】

    评论区回复
    发送邮件shorfng@126.com


    本文版权归作者和本网站共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。


    如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
    • 支付宝扫一扫 向我打赏

    • 你也可以微信 向我打赏

  • 相关阅读:
    使用BitMap进行海量数据去重
    记一次std::process::Child使用过程中碰到的问题
    我的第一篇rust博客
    优秀编程习惯总结
    利用generator模拟协程完美解决异步回调问题
    polymer框架在代码中动态创建需要支持内容分发的自定义元素并挂载到文档中
    属于自己的完美web服务器完成
    web components折腾记
    内边距的妙用
    用js修改带!important的css样式
  • 原文地址:https://www.cnblogs.com/shorfng/p/6652942.html
Copyright © 2020-2023  润新知