【问题标题】:How to Curve a polyline (Google Maps) in IOS?如何在 IOS 中弯曲折线(谷歌地图)?
【发布时间】:2018-09-11 11:27:39
【问题描述】:

我想在两点之间弯曲折线。

Android 在 SphericalUtil 类的帮助下做到这一点。 但在 iOS 中我不知道该怎么做。

对于 Android ,这是一个参考链接: Draw ARC Polyline in Google Map

我希望 iOS 也一样。

【问题讨论】:

  • UIBezierPath?!
  • 您可以使用UIBezierPath 或第三方如github.com/ivan114/IVBezierPathRenderer..
  • 我可以使用 UIBezierPath 绘制路径,但它可以在地图上的两个纬度经度点之间使用吗?

标签: ios google-maps google-maps-sdk-ios google-maps-ios-utils


【解决方案1】:

聚会迟到了,但为将来遇到此问题的其他人添加解决方案:

首先:使用UIBezierPath 绘制曲线

关注UIBezierPath 的扩展。使用在CGPoint.controlpoint(_, _) 中计算的垂直控制点将每对点绘制为四边形曲线。

使用tension 值来改变曲线的锐度。 tension2 生成接近四分之一圆的东西。

extension UIBezierPath {

    static func from(_ points: [CGPoint], tension: CGFloat) -> UIBezierPath {
        let path = UIBezierPath()

        guard let first = points.first, points.count > 1 else {
            return path
        }

        path.move(to: first)

        points
            .pairwise()
            .dropFirst()
            .forEach { a, b in
                path.addQuadCurve(
                    to: b,
                    controlPoint: .controlpoint(a!, b, tension: tension)
                )
            }

        return path
    }
}

extension CGPoint {

    static func controlpoint(_ l: CGPoint, _ r: CGPoint, tension: CGFloat = 2) -> CGPoint {
        midpoint(l, r) + (perpendicular(l, r) / tension)
    }

    static func midpoint(_ l: CGPoint, _ r: CGPoint) -> CGPoint {
        (l + r) / 2
    }

    static func perpendicular(_ l: CGPoint, _ r: CGPoint) -> CGPoint {
        let d = l - r
        return CGPoint(x: -d.y, y: d.x)
    }

    static func + (l: CGPoint, r: CGPoint) -> CGPoint {
        CGPoint(
            x: l.x + r.x,
            y: l.y + r.y
        )
    }

    static func - (l: CGPoint, r: CGPoint) -> CGPoint {
        CGPoint(
            x: l.x - r.x,
            y: l.y - r.y
        )
    }

    static func / (point: CGPoint, divisor: CGFloat) -> CGPoint {
        CGPoint(
            x: point.x / divisor,
            y: point.y / divisor
        )
    }
}

extension Sequence {

    func pairwise() -> Zip2Sequence<[Element?], Self> {
        zip([nil] + map(Optional.some), self)
    }
}

显然,这只涉及CGPoints,而不是CLLocationCoordinate2D 或任何其他与地图相关的数据。不过,可以在视图中绘制:

下一步:定义一个自定义MKOverlayPathRenderer

final class BezierPolylineRenderer: MKOverlayPathRenderer {

    var tension: CGFloat = 2.5

    override func createPath() {
        guard let multiPoint = overlay as? MKMultiPoint else {
            assertionFailure("Expected MKMultiPoint")
            return
        }

        let points = (0 ..< multiPoint.pointCount)
            .map { multiPoint.points()[$0] }
            .map { point(for: $0) }

        path = UIBezierPath.from(points, tension: tension).cgPath
    }

    override func draw(
        _: MKMapRect,
        zoomScale: MKZoomScale,
        in context: CGContext
    ) {
        context.addPath(path)

        applyStrokeProperties(to: context, atZoomScale: zoomScale)

        context.setStrokeColor((strokeColor ?? .gray).cgColor)
        context.setLineWidth(lineWidth / zoomScale)

        context.strokePath()
    }
}

最后:为您的地图添加一个委托以使用自定义渲染器

final class Delegate: NSObject, MKMapViewDelegate {

    func mapView(_: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let polyline = overlay as? MKPolyline {
            let renderer = BezierPolylineRenderer(overlay: polyline)
            renderer.strokeColor = .blue
            renderer.lineWidth = 10
            return renderer
        } else {
            fatalError("Unexpected overlay type: \(overlay)")
        }
    }
}

let delegate = Delegate()

let map = MKMapView()
map.delegate = delegate

map.addOverlay(MKPolyline(coordinates: coordinates, count: coordinates.count))

把它们放在一起,它看起来像这样:

如果您不喜欢兔子跳路径:

...还有其他方法可以计算贝塞尔曲线的控制点:

extension UIBezierPath {

    static func from(_ points: [CGPoint], tension: CGFloat) -> UIBezierPath {
        let path = UIBezierPath()
        guard let first = points.first, points.count > 1 else {
            return UIBezierPath()
        }

        var derivatives: [CGPoint] = []
        for j in 0 ..< points.count {
            let prev = points[max(j - 1, 0)]
            let next = points[min(j + 1, points.count - 1)]
            derivatives.append((next - prev) / tension)
        }

        path.move(to: first)
        for i in 1 ..< points.count {
            let cp1 = points[i - 1] + (derivatives[i - 1] / tension)
            let cp2 = points[i] - (derivatives[i] / tension)
            path.addCurve(to: points[i], controlPoint1: cp1, controlPoint2: cp2)
        }

        return path
    }
}

上述方法在当前点前后扫描,得到两个控制点,路径更平滑:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 2013-04-13
    • 1970-01-01
    • 2018-07-20
    • 1970-01-01
    相关资源
    最近更新 更多