美文网首页
在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