如何使注释在折线上移动

how to make the annotation move over the polyline

我有 3 个注释,我在第一个注释和第二个注释之间绘制折线,但我需要第三个移动到该折线上,但它总是在街道折线上移动到目的地

-我的代码

   func moveDelivery(_ destinationCoordinate : CLLocationCoordinate2D{
        self.deliveryAnnotation.coordinate =      CLLocationCoordinate2DMake(29.959640, 31.270421)
    let sourcePlaceMark = MKPlacemark(coordinate:     self.userAnnotation.coordinate)
    //sourcePlaceMark.title
    let destPlaceMkark = MKPlacemark(coordinate:   self.deliveryAnnotation.coordinate)

    let sourceItem = MKMapItem(placemark: sourcePlaceMark)
    let destItem = MKMapItem(placemark: destPlaceMkark)

    let directionRequest = MKDirections.Request()
    directionRequest.source = sourceItem
    directionRequest.destination = destItem
    directionRequest.transportType = .any

    let direction = MKDirections(request: directionRequest)
    direction.calculate(completionHandler: {
        response, error in
        guard let response = response else {

            if let error = error {
                print(error.localizedDescription)
            } else {
                self.deliveryAnnotation.courseDegrees =   self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
                self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
            }
            return
        }
        guard let primaryRoute = response.routes.first else { return }
        let route = response.routes[0]
        self.mapView.addOverlay(route.polyline, level: .aboveRoads)

        let rekt = route.polyline.boundingMapRect
        self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)

    })

    //
    UIView.animate(withDuration: Double(60), animations: {

        self.deliveryAnnotation.coordinate = destinationCoordinate

    }, completion:  { success in
        if success {

        }
    })
}

您的第三个注释未遵循路线,因为您正在为其设置动画,使它在第一条线和第二条线之间直线移动。尝试从 MKRoute 的折线获取坐标并在每个折线之间设置动画(根据苹果文档 MKRoutes 由坐标组成,但您也可以使用点)

如果您希望它在 60 秒的跨度内进行动画处理:

func moveDelivery(_ destinationCoordinate: CLLocationCoordinate2D) {

    // I don't know why you have the delivery annotation start here, is this for testing?
    deliveryAnnotation.coordinate = CLLocationCoordinate2DMake(29.959640, 31.270421)

    let sourcePlaceMark = MKPlacemark(coordinate: destinationCoordinate)
    let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)

    let directionRequest = MKDirections.Request()
    directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
    directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
    directionRequest.transportType = .any
    let direction = MKDirections(request: directionRequest)

    direction.calculate(completionHandler: {
        response, error in
        guard let response = response else {
            print("MKRequest gave no response")
            if let error = error {
                print(error.localizedDescription)
            } else {
                self.deliveryAnnotation.courseDegrees =   self.getHeadingForDirectionFromCoordinate(self.kitchenAnnotation.coordinate, toLoc: self.userAnnotation.coordinate)
                self.view.transform = CGAffineTransform(rotationAngle:CGFloat(self.deliveryAnnotation.courseDegrees))
            }

            return
        }

        guard let primaryRoute = response.routes.first else {
            print("response has no routes")
            return
        }

        self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)

        let rekt = primaryRoute.polyline.boundingMapRect
        self.mapView.setRegion(MKCoordinateRegion(rekt), animated: true)

        let coordinateArray = primaryRoute.polyline.coordinates

        assert(coordinateArray.count > 0, "coordinate array is empty")

        self.routeCoordinates = coordinateArray

        // initiate recursive animations
        self.coordinateIndex = 0
    })

}

var routeCoordinates = [CLLocationCoordinate2D]()

var avgAnimationTime: Double {
    return 60 / Double(routeCoordinates.count)
}

var coordinateIndex: Int! {
    didSet {
        guard coordinateIndex != routeCoordinates.count else {
            print("animated through all coordinates, stopping function")
            return
        }

        animateToNextCoordinate()
    }
}

func animateToNextCoordinate() {
    let coordinate = routeCoordinates[coordinateIndex]

    UIView.animate(withDuration: avgAnimationTime, animations: {
        self.deliveryAnnotation.coordinate = coordinate
    }, completion:  { _ in
        self.coordinateIndex += 1
        print("moved between coordinates")
    })
}

编辑

确保包含此扩展,否则您将无法获取 MKRoute 的坐标(来源:https://gist.github.com/freak4pc/98c813d8adb8feb8aee3a11d2da1373f

public extension MKMultiPoint {
    var coordinates: [CLLocationCoordinate2D] {
        var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
                                              count: pointCount)

        getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))

        return coords
    }
}

编辑 #2

见上文,编辑原始答案以在前一个完成动画后通过每个坐标进行动画处理。真的很粗糙,但应该可以。

编辑 #3

添加了您的代码以获取 destination 变量以及一些断言和调试打印调用。如果这次不能正常工作,请告诉我您收到了哪些调试消息。

编辑 #4

我刚刚演示了我的代码并且它可以工作。这是我使用的 MapViewController class 以及必要的扩展:

private let reuseId = "deliveryReuseId"

private let userTitle = "user"
private let startingPointTitle = "store"
private let deliveryTitle = "delivery truck"

class MapViewController: UIViewController {

    var mapView: MKMapView!

    // annotations for this demo, replace with your own annotations
    var deliveryAnnotation: MKPointAnnotation = {
        let annotation = MKPointAnnotation()
        annotation.title = deliveryTitle

        return annotation
    }()

    let userAnnotation: MKPointAnnotation = {
        let annotation = MKPointAnnotation()
        annotation.title = userTitle
        annotation.coordinate = CLLocationCoordinate2DMake(29.956694, 31.276854)
        return annotation
    }()

    let startingPointAnnotation: MKPointAnnotation = {
        let annotation = MKPointAnnotation()
        annotation.title = startingPointTitle
        annotation.coordinate = CLLocationCoordinate2DMake(29.959622, 31.270363)
        return annotation
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        loadMapView()
        navigate()
    }

    func loadMapView() {
        // set map
        mapView = MKMapView()
        view = mapView
        mapView.delegate = self
        mapView.register(MKAnnotationView.self, forAnnotationViewWithReuseIdentifier: reuseId)

        // add annotations
        mapView.addAnnotation(userAnnotation)
        mapView.addAnnotation(startingPointAnnotation)
        mapView.addAnnotation(deliveryAnnotation)
    }

    func navigate() {
        let sourcePlaceMark = MKPlacemark(coordinate: startingPointAnnotation.coordinate)
        let destPlaceMkark = MKPlacemark(coordinate: userAnnotation.coordinate)

        let directionRequest = MKDirections.Request()
        directionRequest.source = MKMapItem(placemark: sourcePlaceMark)
        directionRequest.destination = MKMapItem(placemark: destPlaceMkark)
        directionRequest.transportType = .any

        let direction = MKDirections(request: directionRequest)

        direction.calculate(completionHandler: { response, error in
            if let error = error {
                print(error.localizedDescription)

                return
            }

            guard let primaryRoute = response!.routes.first else {
                print("response has no routes")
                return
            }

            self.mapView.addOverlay(primaryRoute.polyline, level: .aboveRoads)
            self.mapView.setRegion(MKCoordinateRegion(primaryRoute.polyline.boundingMapRect), animated: true)

            // initiate recursive animation
            self.routeCoordinates = primaryRoute.polyline.coordinates
            self.coordinateIndex = 0
        })
    }

    var routeCoordinates = [CLLocationCoordinate2D]()

    var avgAnimationTime: Double {
        // to show delivery in 60 second, replace 60 with amount of seconds you'd like to show
        return 60 / Double(routeCoordinates.count)
    }

    var coordinateIndex: Int! {
        didSet {
            guard coordinateIndex != routeCoordinates.count else {
                print("animated through all coordinates, stopping function")
                return
            }

            animateToNextCoordinate()
        }
    }

    func animateToNextCoordinate() {
        let coordinate = routeCoordinates[coordinateIndex]

        UIView.animate(withDuration: avgAnimationTime, animations: {
            self.deliveryAnnotation.coordinate = coordinate
        }, completion:  { _ in
            self.coordinateIndex += 1
        })
    }
}

extension MapViewController: MKMapViewDelegate {

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)

        // replace these images with your own
        switch annotation.title {
        case userTitle:
            annotationView.image = UIImage(named: "user")
        case startingPointTitle:
            annotationView.image = UIImage(named: "store")
        case deliveryTitle:
            annotationView.image = UIImage(named: "deliveryTruck")
        default: break
        }

        return annotationView
    }

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {

        guard overlay is MKPolyline else {
            return MKOverlayRenderer()
        }

        let renderer = MKPolylineRenderer(overlay: overlay)
        renderer.strokeColor = .black
        renderer.lineWidth = 5
        renderer.lineJoin = .round

        return renderer
    }
}

public extension MKMultiPoint {
    var coordinates: [CLLocationCoordinate2D] {
        var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
                                              count: pointCount)

        getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))

        return coords
    }
}