MapKit:当地图pitch不为0时,如何变换位置指示图标使其朝向正确方位
你遇到的偏差不是地球曲率导致的,是你手动做的3D变换没有匹配MapKit的透视投影规则:当相机有俯仰角(pitch≠0)时,地图是透视投影而非正交投影,不在屏幕中心的元素会产生近大远小的透视形变,你仅对annotation自身做旋转没有补偿屏幕位置偏移带来的透视角度差,所以会出现左右偏移时指向偏差。
以下是两种可行的解决方案:
方案1:优化Polyline绘制(推荐)
你之前觉得Polyline闪烁是因为每次都移除重绘,实际上可以用更高效的更新方式:
- 预先创建一条MKPolyline作为全局overlay,仅在初始化时添加一次到地图
- 当位置/航向变化时,直接更新polyline的坐标数组,调用
renderer.setNeedsDisplay()刷新即可,iOS 13及以上系统支持overlay动态更新,完全不会出现闪烁 - Polyline会自动适配MapKit的相机变换、旋转、俯仰,不需要手动做任何3D变换,指向永远和真实航向对齐
方案2:修正Annotation的3D变换
如果你坚持要用自定义图片的MKAnnotationView,可以给旋转角度增加透视补偿修正:
// 原有角度计算 let rotationRadians = getHeadingIndicatorRotation(cameraHeading: mapView.camera.heading, courseHeading: navigation.lastLocation.course) let pitchRadians = Conversions.toRadians(degrees: mapView.camera.pitch) // 新增透视补偿计算 let screenPoint = mapView.convert(annotation.coordinate, toPointTo: mapView) let offsetX = screenPoint.x - mapView.bounds.midX let fovRadians = Conversions.toRadians(degrees: mapView.camera.fieldOfView) let pixelAngle = fovRadians / mapView.bounds.width // 俯仰角越大,补偿系数越高 let compensateAngle = offsetX * pixelAngle * sin(pitchRadians) // 加入补偿后的旋转角度 let t1 = CATransform3DMakeRotation(rotationRadians + compensateAngle, 0, 0, 1) let t2 = CATransform3DMakeRotation(pitchRadians, 1, 0, 0) // 注意变换顺序:先做俯仰旋转,再做航向旋转 view.layer.transform = CATransform3DConcat(t2, t1)
额外优化:可以把航向指示器图片中延伸的虚线部分做成长度自适应,根据当前地图的zoom级别调整图片的scale属性,避免缩放时虚线长度不合理。
内容的提问来源于stack exchange,提问作者user1156670
相关产品推荐
相关产品推荐

