美文网首页iOS开发(OC)
iOS 中打开外带地图进行导航

iOS 中打开外带地图进行导航

作者: 婉卿容若 | 来源:发表于2017-03-13 14:02 被阅读879次

    参考资料

    iOS实现应用外自带地图、高德地图、百度地图导航

    检查本地的地图 app

    主要对国内主流的地图进行检测 => 百度地图,高德地图,腾讯地图

    主要通过 UIApplication.shared.canOpenURL(NSURL(string: "app scheme") as! URL) 方法检测是否安装对应的 app

    首先: 在 info.plist 里添加白名单, 否则无法正常跳转

    白名单.png

    检测代码

            // 检测手机上的地图
            let isBaiduMap = UIApplication.shared.canOpenURL(URL(string: "baidumap://")!)
            let isTencentMap = UIApplication.shared.canOpenURL(URL(string: "sosomap://")!)
            let isGaodeMap = UIApplication.shared.canOpenURL(URL(string: "iosamap://")!)
    
    

    ps: 相应的 app scheme 可以自己去寻找这里有个总结帖

    跳转

    原理: 通过 app scheme 进行应用间跳转 + 通过 URI 的资源地址部分进行指定动作 (类似于web 端的导航实现)
    以下是各个平台关于跳转 URI 规则的说明地址
    百度地图 URL API
    高德地图 URL API
    腾讯地图 URL API

    我的实现

    百度地图:

    
             alertViewController.addAction(showRoute)
            
            if isBaiduMap {
                
                let baiduAction = UIAlertAction(title: "百度地图", style: .default) { (action) in
                    
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        return
                    }
    
                    let ori = self.getCurrentLocation().origin!
                    
                    let str = "baidumap://map/direction?origin=\(ori.latitude),\(ori.longitude)&destination=\(self.cLLocation!.latitude),\(self.cLLocation!.longitude)&name=目的地&mode=driving&coord_type=gcj02&src=浩优服务家"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(baiduAction)
            }
    
    

    说明:

    1. getCurrentLocation() 是我的位置获取的函数,与要说的没有太大关系,下面所有代码里会出现
    2. 跳转是传递了当前经纬度和目的地经纬度, 这些需要自己去获取
    3. 这里使用了UIAlertController,因为我的 app 系统要求是 iOS9.0及以上.

    腾讯地图:

              if isTencentMap {
                
                let tencentAction = UIAlertAction(title: "腾讯地图", style: .default) { (action) in
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        return
                    }
                    
                    let ori = self.getCurrentLocation().origin!
                    
                    let str = "qqmap://map/routeplan?type=drive&from=我的位置&fromcoord=\(ori.latitude),\(ori.longitude)&to=目的地&tocoord=\(self.cLLocation!.latitude),\(self.cLLocation!.longitude)&policy=0&referer=浩优服务家"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(tencentAction)
            }
    

    说明:

    1. 这里跳转是用了 qqmap://进行跳转,因为用sosomap://进行跳转后总是导航失败,可能是没有开发对应的接口. 其次,为何用qqmap://可以实现跳转并导航成功,没弄清楚.我尝试用let isTencentMap = UIApplication.shared.canOpenURL(URL(string: "qqmap://")!)检测腾讯地图,发现检测不到.所以导致了上下的不一致

    高德地图:

            
            if isGaodeMap {
                
                let gaodeAction = UIAlertAction(title: "高德地图", style: .default) { (action) in
                    
                    //                guard self.getCurrentLocation().isUpdatedSuccess else{
                    //                    return
                    //                }
                    //
                    //                let ori = self.getCurrentLocation().origin!
                    
                    //&slat=\(ori.latitude)&slon=\(ori.longitude)&sname=我的位置&did=BGVIS2&d
                    let str = "iosamap://navi?sourceApplication=浩优服务家&backScheme=hoyoServicer&lat=\(self.cLLocation!.latitude)&lon=\(self.cLLocation!.longitude)&name=目的地&dev=0&style=2"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(gaodeAction)
            }
    

    说明:

    1. 高德地图不需要传递当前坐标

    苹果地图:

            let appleAction = UIAlertAction(title: "苹果地图", style: .default) { (action) in
                
                //使用自带地图导航
                guard let des = self.cLLocation else{
                    return
                }
                let destinationPlaceMark = MKPlacemark(coordinate: des, addressDictionary: nil)
                let destinationMapItem = MKMapItem(placemark: destinationPlaceMark)
                destinationMapItem.name = "目的地"
                
                let currentLocation = MKMapItem.forCurrentLocation()
                currentLocation.name = "我的位置"
                MKMapItem.openMaps(with: [currentLocation, destinationMapItem], launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsShowsTrafficKey: NSNumber.init(value: true)])
            }
            alertViewController.addAction(appleAction)
    

    说明:

    1. 苹果地图是自带地图,不需要检测.如果你删除了,打开时会弹出提示让你重新安装
    2. 苹果视图在中国用的数据是高德的数据

    规划路线

            let showRoute = UIAlertAction(title: routeTitle, style: .default) { (action) in
                
                
                if self.isShowRoute{
                    
                    guard let destination = self.cLLocation else{
                        
                        return
                    }
                    
                    MBProgressHUD.showAdded(to: self.mkMapView, animated: true)
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        
                        MBProgressHUD.hide(for: self.mkMapView, animated: true)
                        return
                    }
                    
                    self.makeRoute(self.getCurrentLocation().origin!, destination)
                }else{
                    
                    //                if let overlays = self.myOverlays {
                    //                   // 移除路线 -- 不成功舍弃
                    //                    self.mkMapView.removeOverlays(overlays)
                    //                    self.myOverlays = nil
                    //                }
                    
                }
                
                //  self.isShowRoute = !self.isShowRoute
                
            }
            
            alertViewController.addAction(showRoute)
    

    主要实现

    func makeRoute(_ origin: CLLocationCoordinate2D, _ destination: CLLocationCoordinate2D) {
            
            let originPlaceMark = MKPlacemark(coordinate: origin, addressDictionary: nil)
            let originMapItem = MKMapItem(placemark: originPlaceMark)
            
            let destinationPlaceMark = MKPlacemark(coordinate: destination, addressDictionary: nil)
            let destinationMapItem = MKMapItem(placemark: destinationPlaceMark)
            
            let request = MKDirectionsRequest()
            request.source = originMapItem
            request.destination = destinationMapItem
            
            // pull request to server of apple
            
            // 用于发送请求去服务器,获取规划好的路线
            let directs = MKDirections(request: request)
            directs.calculate { (response, error) in
                
                // 获取所有规划路径
                let routes = response?.routes
                let route = routes?.last
                
                // 保存路线中的每一步
                let steps = route?.steps
                
                guard let _ = steps else{
                    return
                }
                for step in steps!{
                    
                    // 绘制遮盖打印到地图上
                    self.mkMapView.add(step.polyline, level: .aboveRoads)
                    
                  //  self.myOverlays?.append(step.polyline)
                }
            }
            
            
        }
    

    MKMapViewDelegate代理方法

     // 返回指定的遮盖模型所对应的遮盖视图
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            
            // 针对线段,系统有提供好的遮盖视图
            let render = MKPolylineRenderer(polyline: overlay as! MKPolyline)
            
            // 配置遮盖的宽度-颜色
            render.lineWidth = 5.0
            render.strokeColor = UIColor.red
            
            MBProgressHUD.hide(for: self.mkMapView, animated: true)
            return render
        }
    

    完整代码

    import UIKit
    import MapKit
    import JPSThumbnailAnnotation
    import MBProgressHUD
    class ZBMapViewController: UIViewController {
        
        
        @IBAction func BackClick(_ sender: AnyObject) {
            self.dismiss(animated: true, completion: nil)
        }
        @IBOutlet var mkMapView: MKMapView!
        
        var myOverlays: [MKOverlay]? = nil // 路线上步数集合
        var isShowRoute = true
        
        
        fileprivate var cLLocation:CLLocationCoordinate2D?
        fileprivate var cLLocationStr:String?
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.title="位置详情"
            mkMapView.mapType=MKMapType.standard//标准模式
            mkMapView.showsUserLocation=true//显示自己
            
            mkMapView.delegate=self
            mkMapView.isZoomEnabled = true//支持缩放
            if cLLocation != nil {
                let pos = cLLocation//CLLocationCoordinate2D(latitude: 39.931203, longitude: 116.395573)
                let viewRegion = MKCoordinateRegionMakeWithDistance(pos!, 5000, 5000)//以pos为中心,显示2000米
                let adjustedRegion = mkMapView.regionThatFits(viewRegion)//适配map view的尺寸
                mkMapView.setRegion(adjustedRegion, animated: true)
                
                //            let thumbnail = JPSThumbnail()
                //            thumbnail.image = UIImage(named: "positionNewIcon")// ("position")
                //            thumbnail.title = cLLocationStr ?? ""
                //            thumbnail.subtitle = ""
                //            thumbnail.coordinate = cLLocation!
                //            thumbnail.disclosureBlock = { NSLog("selected Empire") }
                //            mkMapView.addAnnotation(JPSThumbnailAnnotation(thumbnail: thumbnail))
                //[mapView addAnnotation:[JPSThumbnailAnnotation annotationWithThumbnail:thumbnail]];
                
                let pointAnnotation = MKPointAnnotation()
                pointAnnotation.coordinate = cLLocation!
                mkMapView.addAnnotation(pointAnnotation)
                
            }
            
            setupBottomView(cLLocationStr ?? "")
            
            
            // Do any additional setup after loading the view.
        }
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(true)
            
        }
        convenience  init(location:CLLocationCoordinate2D?,locationString:String) {
            
            var nibNameOrNil = String?("ZBMapViewController")
            if Bundle.main.path(forResource: nibNameOrNil, ofType: "xib") == nil
            {
                nibNameOrNil = nil
            }
            self.init(nibName: nibNameOrNil, bundle: nil)
            self.cLLocation=location
            self.cLLocationStr=locationString
            
        }
        required init(coder aDecoder: NSCoder) {
            
            fatalError("init(coder:) has not been implemented")
            
        }
        override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?){
            
            super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
            
        }
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
        
    }
    
    // MARK: - custom methods
    
    extension ZBMapViewController {
        
        func setupBottomView(_ title: String){
            
            let h: CGFloat = 80
            
            let bottomView = UIView(frame: CGRect(x: 0, y: MainScreenBounds.height-h, width: MainScreenBounds.width, height: h))
            bottomView.backgroundColor = UIColor.white
            view.addSubview(bottomView)
            
            let contentLabel = UILabel()
            contentLabel.text = title
            contentLabel.numberOfLines = 0
            bottomView.addSubview(contentLabel)
            contentLabel.snp.makeConstraints { (make) in
                make.leading.equalTo(15)
                make.centerY.equalToSuperview()
            }
            
            let btn = UIButton(type: .custom)
            btn.setImage(UIImage(named:"goAddress"), for: .normal)
            btn.backgroundColor = UIColor.cyan
            btn.layer.masksToBounds = true
            btn.layer.cornerRadius = (h-30)/2.0
            btn.addTarget(self, action: #selector(showMap), for: .touchUpInside)
            bottomView.addSubview(btn)
            btn.snp.makeConstraints { (make) in
                make.leading.equalTo(contentLabel.snp.trailing).offset(20)
                make.trailing.equalTo(-15)
                make.centerY.equalToSuperview()
                make.width.equalTo(h-30)
                make.height.equalTo(h-30)
            }
            
        }
        
        func makeRoute(_ origin: CLLocationCoordinate2D, _ destination: CLLocationCoordinate2D) {
            
            let originPlaceMark = MKPlacemark(coordinate: origin, addressDictionary: nil)
            let originMapItem = MKMapItem(placemark: originPlaceMark)
            
            let destinationPlaceMark = MKPlacemark(coordinate: destination, addressDictionary: nil)
            let destinationMapItem = MKMapItem(placemark: destinationPlaceMark)
            
            let request = MKDirectionsRequest()
            request.source = originMapItem
            request.destination = destinationMapItem
            
            // pull request to server of apple
            
            // 用于发送请求去服务器,获取规划好的路线
            let directs = MKDirections(request: request)
            directs.calculate { (response, error) in
                
                // 获取所有规划路径
                let routes = response?.routes
                let route = routes?.last
                
                // 保存路线中的每一步
                let steps = route?.steps
                
                guard let _ = steps else{
                    return
                }
                for step in steps!{
                    
                    // 绘制遮盖打印到地图上
                    self.mkMapView.add(step.polyline, level: .aboveRoads)
                    
                    // self.myOverlays?.append(step.polyline)
                }
            }
            
            
        }
        
        // 判断是否获取到当前位置
        func getCurrentLocation() -> (isUpdatedSuccess: Bool, origin: CLLocationCoordinate2D?) {
            
            let origin = self.mkMapView.userLocation.location?.coordinate
            
            guard let _ = origin else{
                
                let alertView=SCLAlertView()
                alertView.addButton("确定", action: {})
                alertView.showWait("提示", subTitle: "正在定位当前位置,请稍等...")
                
                return (false, nil)
            }
            
            return (true, origin)
            
        }
        
    }
    
    // MARK: - event response
    
    extension ZBMapViewController {
        
        func showMap(){
            
            guard let _ = cLLocation else {
                let alertView=SCLAlertView()
                alertView.addButton("确定", action: {})
                alertView.showWait("提示", subTitle: "未获取到目标坐标")
                
                return
            }
            
            // 检测手机上的地图
            let isBaiduMap = UIApplication.shared.canOpenURL(URL(string: "baidumap://")!)
            let isTencentMap = UIApplication.shared.canOpenURL(URL(string: "sosomap://")!)
            let isGaodeMap = UIApplication.shared.canOpenURL(URL(string: "iosamap://")!)
            
            let alertViewController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
            
            let routeTitle = "显示路线"
            if !isShowRoute {
                
                // routeTitle = "隐藏路线"
            }
            
            let showRoute = UIAlertAction(title: routeTitle, style: .default) { (action) in
                
                
                if self.isShowRoute{
                    
                    guard let destination = self.cLLocation else{
                        
                        return
                    }
                    
                    MBProgressHUD.showAdded(to: self.mkMapView, animated: true)
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        
                        MBProgressHUD.hide(for: self.mkMapView, animated: true)
                        return
                    }
                    
                    self.makeRoute(self.getCurrentLocation().origin!, destination)
                }else{
                    
                    //                if let overlays = self.myOverlays {
                    //                   // 移除路线 -- 不成功舍弃
                    //                    self.mkMapView.removeOverlays(overlays)
                    //                    self.myOverlays = nil
                    //                }
                    
                }
                
                //  self.isShowRoute = !self.isShowRoute
                
            }
            
            alertViewController.addAction(showRoute)
            
            if isBaiduMap {
                
                let baiduAction = UIAlertAction(title: "百度地图", style: .default) { (action) in
                    
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        return
                    }
                    
                    let ori = self.getCurrentLocation().origin!
                    
                    let str = "baidumap://map/direction?origin=\(ori.latitude),\(ori.longitude)&destination=\(self.cLLocation!.latitude),\(self.cLLocation!.longitude)&name=目的地&mode=driving&coord_type=gcj02&src=浩优服务家"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(baiduAction)
            }
            
            if isTencentMap {
                
                let tencentAction = UIAlertAction(title: "腾讯地图", style: .default) { (action) in
                    
                    guard self.getCurrentLocation().isUpdatedSuccess else{
                        return
                    }
                    
                    let ori = self.getCurrentLocation().origin!
                    
                    let str = "qqmap://map/routeplan?type=drive&from=我的位置&fromcoord=\(ori.latitude),\(ori.longitude)&to=目的地&tocoord=\(self.cLLocation!.latitude),\(self.cLLocation!.longitude)&policy=0&referer=浩优服务家"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(tencentAction)
            }
            
            if isGaodeMap {
                
                let gaodeAction = UIAlertAction(title: "高德地图", style: .default) { (action) in
                    
                    //                guard self.getCurrentLocation().isUpdatedSuccess else{
                    //                    return
                    //                }
                    //
                    //                let ori = self.getCurrentLocation().origin!
                    
                    //&slat=\(ori.latitude)&slon=\(ori.longitude)&sname=我的位置&did=BGVIS2&d
                    let str = "iosamap://navi?sourceApplication=浩优服务家&backScheme=hoyoServicer&lat=\(self.cLLocation!.latitude)&lon=\(self.cLLocation!.longitude)&name=目的地&dev=0&style=2"
                    // 对汉字进行转码 stringByAddingPercentEscapesUsingEncoding => iOS9.0 addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    let urlStr = str.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
                    
                    guard let us = urlStr, let u = URL(string: us) else{
                        return
                    }
                    UIApplication.shared.openURL(u)
                }
                alertViewController.addAction(gaodeAction)
            }
            
            let appleAction = UIAlertAction(title: "苹果地图", style: .default) { (action) in
                
                //使用自带地图导航
                guard let des = self.cLLocation else{
                    return
                }
                let destinationPlaceMark = MKPlacemark(coordinate: des, addressDictionary: nil)
                let destinationMapItem = MKMapItem(placemark: destinationPlaceMark)
                destinationMapItem.name = "目的地"
                
                let currentLocation = MKMapItem.forCurrentLocation()
                currentLocation.name = "我的位置"
                MKMapItem.openMaps(with: [currentLocation, destinationMapItem], launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsShowsTrafficKey: NSNumber.init(value: true)])
            }
            alertViewController.addAction(appleAction)
            
            let cancelAction = UIAlertAction(title: "取消", style: .cancel) { (cation) in}
            alertViewController.addAction(cancelAction)
            
            self.present(alertViewController, animated: true) {}
        }
    }
    
    
    // MARK: - MKMapViewDelegate
    
    extension ZBMapViewController: MKMapViewDelegate {
        
        func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
            if view.conforms(to: JPSThumbnailAnnotationViewProtocol.self) {
                (view as! JPSThumbnailAnnotationViewProtocol).didSelectAnnotationView(inMap: mapView)
            }
        }
        func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
            if view.conforms(to: JPSThumbnailAnnotationViewProtocol.self) {
                (view as! JPSThumbnailAnnotationViewProtocol).didDeselectAnnotationView(inMap: mapView)
            }
        }
        
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            if annotation.conforms(to: JPSThumbnailAnnotationProtocol.self) {
                return (annotation as! JPSThumbnailAnnotationProtocol).annotationView(inMap: mapView)
            }
            return nil
            
        }
        
        // 返回指定的遮盖模型所对应的遮盖视图
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            
            // 针对线段,系统有提供好的遮盖视图
            let render = MKPolylineRenderer(polyline: overlay as! MKPolyline)
            
            // 配置遮盖的宽度-颜色
            render.lineWidth = 5.0
            render.strokeColor = UIColor.red
            
            MBProgressHUD.hide(for: self.mkMapView, animated: true)
            return render
        }
        
    }
    

    相关文章

      网友评论

      本文标题:iOS 中打开外带地图进行导航

      本文链接:https://www.haomeiwen.com/subject/jwbfnttx.html