美文网首页
在SwiftUI中使用地图以及生成导航路线

在SwiftUI中使用地图以及生成导航路线

作者: 张林建 | 来源:发表于2022-07-19 17:31 被阅读0次

    在学习SwiftUI的过程中涉及到需要使用地图,并且在地图上绘制两点之间的导航路线。发现使用SwiftUI直接自带的Map()好像难以实现绘制导航路线,所以还是从MKMapView进行实现。以下是实现的代码,仅供参考。代码中实现的是从用户当前的位置到目的地位置的导航路线。

    首先新建一个SwiftUIView文件,我们将其命名为MyMapView,然后将其修改为

    struct MyMapView: UIViewRepresentable {
        let destination: CLLocationCoordinate2D
    
        func makeUIView(context: Context) -> MKMapView {
              MKMapView(frame: .zero)
        }
        
        func updateUIView(_ uiView: MKMapView, context: Context) {      
            let coordinate = destination
            let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            uiView.setRegion(region, animated: true)        
        }
    }
    

    这样,我们就已经可以通过调用以下代码在其他View中绘制地图了

    MyMapView(destination: )
    

    接下来,我们需要添加MKMapViewDelegate

    class MyMapViewCoordinator: NSObject, MKMapViewDelegate {
        var myMapViewController: MyMapView
        
        init(_ control: MyMapView){
            self.myMapViewController = control
        }
        
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            if let routePolyline = overlay as? MKPolyline {
                let renderer = MKPolylineRenderer(polyline: routePolyline)
                renderer.strokeColor = UIColor.systemBlue
                renderer.lineWidth = 6
                return renderer
            }
            return MKOverlayRenderer()
        }
    
    }
    

    同时,我们需要在MyMapView中添加以下函数

    func makeCoordinator() -> MyMapViewCoordinator {
            MyMapViewCoordinator(self)
     }
    

    并且需要在updateUIView方法中添加以下代码以设置delegate

    uiView.delegate = context.coordinator
    

    到目前为止定义的delegate的主要作用是设置我们之后绘制的路径的颜色以及粗细

    接下来,我们在MyMapView中添加以下函数来进行导航路线绘制

    func drawRoute(_ uiView: MKMapView,  _ start:CLLocationCoordinate2D, _ end:CLLocationCoordinate2D, isChangeRegion:Bool){
            let startPlacemark = MKPlacemark(coordinate: start)
            let endPlacemark = MKPlacemark(coordinate: end)
            let startMapItem = MKMapItem(placemark: startPlacemark)
            let endMapItem = MKMapItem(placemark: endPlacemark)
            
            let directionRequest = MKDirections.Request()
            directionRequest.source = startMapItem
            directionRequest.destination = endMapItem
            directionRequest.transportType = .automobile
            let directions = MKDirections(request: directionRequest)
            
            directions.calculate(completionHandler: {(response, err) in
                guard let response = response else {
                    if let err = err{
                        print("Error: \(err)")
                    }
                    return
                }
                let route = response.routes[0]
                uiView.removeOverlays(uiView.overlays)
                uiView.addOverlay(route.polyline, level: .aboveRoads)
                if isChangeRegion{
                    let rect = route.polyline.boundingMapRect
                    uiView.setRegion(MKCoordinateRegion(rect), animated: true)
                }
            })
        }
    

    上述代码中isChangeRegion的主要目的是为了在之后调用时,只在第一次打开地图时,才会有地图缩放的动画。

    为了实时绘制从用户当前位置到目的位置的路线,我们还需要在MyMapViewCoordinator增加一个delegate方法

    var isFirst = true
    
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            let start = userLocation.coordinate
            self.myMapViewController.drawRoute(mapView, start, self.myMapViewController.destination, isChangeRegion: isFirst)
            isFirst = false
        }
    

    同时,我们需要在updateUIView方法中添加

    uiView.showsUserLocation = true
    

    至此,当我们在使用MyMapView(destination: )显示地图时,就会看到用户当前的位置以及目的位置之间的导航路线

    另外,如果要显示目的位置的图钉,可以在updateUIView中添加以下代码

    uiView.addAnnotations(landmarks ?? [])
    

    其中landamarks相关的定义如下

    class LandmarkAnnotation: NSObject, MKAnnotation {
        let title: String?
        let subtitle: String?
        let coordinate: CLLocationCoordinate2D
        
        init(title:String?, subtitle:String?, coordinate:CLLocationCoordinate2D){
            self.title = title
            self.subtitle = subtitle
            self.coordinate = coordinate
        }
    }
    
    var landmarks: [LandmarkAnnotation]?
    

    完整的代码如下:
    LandmarkAnnotation.swift

    import SwiftUI
    import MapKit
    
    class LandmarkAnnotation: NSObject, MKAnnotation {
        let title: String?
        let subtitle: String?
        let coordinate: CLLocationCoordinate2D
        
        init(title:String?, subtitle:String?, coordinate:CLLocationCoordinate2D){
            self.title = title
            self.subtitle = subtitle
            self.coordinate = coordinate
        }
    }
    

    MyMapView.swift

    import SwiftUI
    import MapKit
    
    struct MyMapView: UIViewRepresentable {
       let destination: CLLocationCoordinate2D
        
        var landmarks: [LandmarkAnnotation]?
        
        func makeUIView(context: Context) -> MKMapView {
            MKMapView(frame: .zero)
        }
        
        func updateUIView(_ uiView: MKMapView, context: Context) {
            
            let coordinate = destination
            let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            uiView.setRegion(region, animated: true)
            
            uiView.delegate = context.coordinator
            
            uiView.showsUserLocation = true
            uiView.addAnnotations(landmarks ?? [])  
        }
        
        func makeCoordinator() -> MyMapViewCoordinator {
            MyMapViewCoordinator(self)
        }
        
        func drawRoute(_ uiView: MKMapView,  _ start:CLLocationCoordinate2D, _ end:CLLocationCoordinate2D, isChangeRegion:Bool){
            let startPlacemark = MKPlacemark(coordinate: start)
            let endPlacemark = MKPlacemark(coordinate: end)
            let startMapItem = MKMapItem(placemark: startPlacemark)
            let endMapItem = MKMapItem(placemark: endPlacemark)
            
            let directionRequest = MKDirections.Request()
            directionRequest.source = startMapItem
            directionRequest.destination = endMapItem
            directionRequest.transportType = .automobile
            let directions = MKDirections(request: directionRequest)
            
            directions.calculate(completionHandler: {(response, err) in
                guard let response = response else {
                    if let err = err{
                        print("Error: \(err)")
                    }
                    return
                }
                let route = response.routes[0]
                uiView.removeOverlays(uiView.overlays)
                uiView.addOverlay(route.polyline, level: .aboveRoads)
                if isChangeRegion{
                    let rect = route.polyline.boundingMapRect
                    uiView.setRegion(MKCoordinateRegion(rect), animated: true)
                }
            })
        }
    }
    
    class MyMapViewCoordinator: NSObject, MKMapViewDelegate {
        var myMapViewController: MyMapView
        
        var isFirst = true
        
        init(_ control: MyMapView){
            self.myMapViewController = control
        }
        
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            if let routePolyline = overlay as? MKPolyline {
                let renderer = MKPolylineRenderer(polyline: routePolyline)
                renderer.strokeColor = UIColor.systemBlue
                renderer.lineWidth = 6
                return renderer
            }
            return MKOverlayRenderer()
        }
        
        func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            let start = userLocation.coordinate
            self.myMapViewController.drawRoute(mapView, start, self.myMapViewController.destination, isChangeRegion: isFirst)
            isFirst = false
        }
    }
    

    调用方式

     MyMapView(destination: , landmarks: )
    

    相关文章

      网友评论

          本文标题:在SwiftUI中使用地图以及生成导航路线

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