| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- //
- // 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..<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 {
- // 只绘制每隔step个箭头
- if arrowIndex % step == 0 {
- 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)
- }
- 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()
- }
- }
|