MKPolylineRenderer绘制箭头模糊及缩放异常问题求助
问题修复:MKPolyline箭头绘制模糊、缩放异常问题
原始问题
通过重写draw方法在MKPolyline上绘制箭头,功能可正常实现,但存在以下问题:
- 绘制出的线条存在模糊问题(示例图:
) - 当地图缩小至
zoomScale < 0.05时箭头不显示 - 线条宽度会随地图缩小而增大
问题分析
当前实现的核心问题根源:
- 线条模糊:未适配Retina屏幕的像素比例,绘制时没有处理上下文缩放,导致矢量图形光栅化后出现像素模糊;同时坐标计算未与屏幕scale关联,存在像素偏移。
- 小缩放级别箭头消失:硬编码了
zoomScale >= 0.05的显示阈值,低于该值直接跳过箭头绘制。 - 线宽缩放异常:使用
5/zoomScale设置线宽,zoomScale越小(地图越远),线宽数值越大,导致视觉上线条越来越粗。
修复代码
class ArrowPolylineRenderer: MKPolylineRenderer { override func draw(_ mapRect: MKMapRect, zoomScale: MKZoomScale, in context: CGContext) { guard self.polyline.pointCount >= 2 else { return } let mapPoints = self.polyline.points() let startPoint = mapPoints[0] let endPoint = mapPoints[1] // 适配Retina屏幕,解决模糊问题 let screenScale = UIScreen.main.scale context.saveGState() context.scaleBy(x: screenScale, y: screenScale) context.setShouldAntialias(true) let path = CGMutablePath() // 调整箭头显示的最小缩放阈值,可根据需求修改 let minZoomForArrow: MKZoomScale = 0.01 if zoomScale >= minZoomForArrow { let midMapPoint = MKMapPoint(x: (startPoint.x + endPoint.x)/2, y: (startPoint.y + endPoint.y)/2) // 转换为适配屏幕scale的逻辑坐标 let midPoint = self.point(for: midMapPoint).applying(CGAffineTransform(scaleX: 1/screenScale, y: 1/screenScale)) // 计算线段方向角,修正MKMapPoint与UIKit的坐标系差异 let deltaX = endPoint.x - startPoint.x let deltaY = endPoint.y - startPoint.y let bearingAngle = atan2(deltaY, deltaX) - .pi/2 // 箭头大小随缩放动态调整,保证视觉一致性 let arrowScale = 1 / zoomScale / screenScale let arrowHalfWidth = 60 * arrowScale let arrowHeight = 80 * arrowScale // 箭头顶点坐标(以中点为原点) let arrowVertices = [ CGPoint(x: -arrowHalfWidth, y: arrowHeight), CGPoint(x: arrowHalfWidth, y: 0), CGPoint(x: -arrowHalfWidth, y: -arrowHeight) ] // 构建变换:平移到中点 → 旋转到对应方向 let transform = CGAffineTransform(translationX: midPoint.x, y: midPoint.y) .rotated(by: CGFloat(bearingAngle)) path.addLines(between: arrowVertices, transform: transform) } // 绘制主线段,计算适配的线宽 let baseLineWidth: CGFloat = 5 let adjustedLineWidth = baseLineWidth / screenScale / zoomScale let startCGPoint = self.point(for: startPoint).applying(CGAffineTransform(scaleX: 1/screenScale, y: 1/screenScale)) let endCGPoint = self.point(for: endPoint).applying(CGAffineTransform(scaleX: 1/screenScale, y: 1/screenScale)) path.addLines(between: [startCGPoint, endCGPoint]) context.setStrokeColor(UIColor.yellow.cgColor) context.setLineWidth(adjustedLineWidth) context.addPath(path) context.strokePath() // 恢复上下文状态 context.restoreGState() } }
关键修复说明
- 解决模糊问题:
- 使用
saveGState()和restoreGState()管理上下文状态,先按屏幕scale放大上下文,绘制完成后恢复; - 开启抗锯齿,提升线条边缘平滑度;
- 将地图坐标转换为适配scale的逻辑坐标,避免像素偏移。
- 使用
- 调整箭头显示逻辑:
- 降低箭头显示的最小zoomScale阈值(改为0.01),支持更缩小的地图视图;
- 箭头大小通过统一缩放系数计算,保证不同缩放级别下视觉大小一致;
- 修正方向角计算,适配UIKit坐标系(MKMapPoint的y轴向下,UIKit向上,需减去π/2调整角度)。
- 修复线宽异常:
- 使用
baseLineWidth / screenScale / zoomScale计算线宽,确保地图缩放时线宽的视觉大小相对稳定; - 移除了冗余的
applyStrokeProperties重写,避免上下文设置冲突。
- 使用
内容的提问来源于stack exchange,提问作者tommy-b-10
相关产品推荐
相关产品推荐

