| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- //
- // ArrowPolylineRenderer.swift
- // map_mapkit_ios
- //
- // Created by 诺诺诺的言 on 2025/7/14.
- //
- import UIKit
- import MapKit
- import Combine
- class ArrowPolylineRenderer: MKPolylineRenderer {
- // 箭头属性
- var arrowColor: UIColor = .red
- var arrowSize: CGFloat = 8
- var arrowSpacing: CGFloat = 30
-
- // 边框属性
- var borderWidth: CGFloat = 0 {
- didSet {
- invalidatePath()
- }
- }
-
- var borderColor: UIColor = .clear {
- didSet {
- invalidatePath()
- }
- }
-
- override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
- // 1. 绘制边框(如果启用)
- if borderWidth > 0 {
- context.saveGState()
-
- // 设置边框样式
- context.setLineWidth(lineWidth + borderWidth * 2)
- context.setStrokeColor(borderColor.cgColor)
- context.setLineCap(lineCap)
- context.setLineJoin(lineJoin)
-
- // 创建路径并描边
- let path = createPath(for: polyline, in: mapRect, zoomScale: zoomScale)
- context.addPath(path)
- context.strokePath()
-
- context.restoreGState()
- }
-
- // 2. 绘制主线条
- super.draw(mapRect, zoomScale: zoomScale, in: context)
-
- // 3. 绘制箭头
- drawArrows(mapRect: mapRect, zoomScale: zoomScale, in: context)
- }
-
-
- private func createPath(for polyline: MKPolyline, in mapRect: MKMapRect, zoomScale: MKZoomScale) -> CGPath {
- let path = CGMutablePath()
- let points = polyline.points()
-
- // 添加第一个点
- let firstPoint = self.point(for: points[0])
- path.move(to: firstPoint)
-
- // 添加剩余点
- for i in 1..<polyline.pointCount {
- let point = self.point(for: points[i])
- path.addLine(to: point)
- }
-
- return path
- }
-
- private func drawArrows(mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) {
- guard polyline.pointCount >= 2 else { return }
-
- let scaledArrowSize = arrowSize / zoomScale
- let scaledArrowSpacing = arrowSpacing / zoomScale
-
- var distance: CGFloat = 0
- let points = polyline.points()
-
- for i in 0..<polyline.pointCount-1 {
- let startPoint = point(for: points[i])
- let endPoint = point(for: points[i+1])
-
- let dx = endPoint.x - startPoint.x
- let dy = endPoint.y - startPoint.y
- let segmentLength = hypot(dx, dy)
-
- guard segmentLength > 2 / zoomScale else { continue }
-
- let arrowCount = Int(floor((distance + segmentLength) / scaledArrowSpacing))
-
- for j in 0..<arrowCount {
- let position = CGFloat(j) * scaledArrowSpacing - distance
- let ratio = position / segmentLength
-
- guard ratio >= 0 && ratio <= 1 else { continue }
-
- let arrowPoint = CGPoint(
- x: startPoint.x + dx * ratio,
- y: startPoint.y + dy * ratio
- )
-
- let angle = atan2(dy, dx)
- drawSingleArrow(at: arrowPoint, angle: angle, size: scaledArrowSize, in: context)
- }
-
- distance = (distance + segmentLength).truncatingRemainder(dividingBy: scaledArrowSpacing)
- }
- }
-
- private func drawSingleArrow(at point: CGPoint, angle: CGFloat, size: CGFloat, in context: CGContext) {
- context.saveGState()
- context.translateBy(x: point.x, y: point.y)
- context.rotate(by: angle)
-
- let arrowPath = UIBezierPath()
- arrowPath.move(to: CGPoint(x: 3.2, y: 0))
- arrowPath.addLine(to: CGPoint(x: 0, y: 2.6))
- arrowPath.addLine(to: CGPoint(x: -3.2, y: 2.6))
- arrowPath.addLine(to: CGPoint(x: 0, y: 0))
- arrowPath.addLine(to: CGPoint(x: -3.2, y: -2.6))
- arrowPath.addLine(to: CGPoint(x: 0, y: -2.6))
- arrowPath.close()
-
- context.setFillColor(arrowColor.cgColor)
- context.addPath(arrowPath.cgPath)
- context.fillPath()
- context.restoreGState()
- }
- }
|