// // 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 var borderColor: UIColor = .clear override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { // 1. 绘制边框 if borderWidth > 0 { context.saveGState() // 边框宽度 = 主线条宽度 + 2倍边框宽度(左右各扩展 borderWidth) let borderLineWidth = lineWidth + borderWidth * 2 context.setLineWidth(borderLineWidth) context.setStrokeColor(borderColor.cgColor) context.setLineCap(lineCap) // 保持与主线条一致的线帽样式 context.setLineJoin(lineJoin) // 保持与主线条一致的拐角样式 // 绘制边框线条 super.draw(mapRect, zoomScale: zoomScale, in: context) context.restoreGState() } // 2. 绘制主轨迹线 super.draw(mapRect, zoomScale: zoomScale, in: context) // 3. 绘制箭头(数量减半) drawArrows(mapRect: mapRect, zoomScale: zoomScale, in: context) } 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() // 使用stride跳过每隔一个的箭头 let step = 2 // 跳过间隔 var arrowIndex = 0 for i in 0.. 2 / zoomScale else { continue } let arrowCount = Int(floor((distance + segmentLength) / scaledArrowSpacing)) for j in 0..= 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) } arrowIndex += 1 } 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() } }