在学习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: )
网友评论