iOS开发(OC)

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

2017-03-13  本文已影响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
    }
    
}
上一篇下一篇

猜你喜欢

热点阅读