You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS16及以上MKMapView随动缩放圆圈闪烁问题求助

问题:MKMapView中心圆圈随地图移动/缩放时闪烁

我需要在MKMapView上添加一个始终处于地图中心的圆圈,要求随用户平移地图同步移动,随地图缩放同步缩放。目前通过获取地图中心坐标实现了功能,但每次坐标变化时都要移除并重新添加MKCircle overlay,导致圆圈出现明显闪烁。

期望目标

在MKMapView上方添加可随地图同步移动/缩放的圆圈/标注,无闪烁、流畅显示。


现有代码

地图初始化与手势添加

func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator
    mapView.isUserInteractionEnabled = true
    mapView.showsUserLocation = true
    mapView.showsCompass = false

    let panGesture = UIPanGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.addPinBasedOnGesture(_:)))
    panGesture.delegate = context.coordinator
    mapView.addGestureRecognizer(panGesture)
    
    return mapView
}

更新圆圈坐标的方法

func updateMovingCircle(regionToUpdate: MKCoordinateRegion, view: MKMapView) {
    DispatchQueue.main.async {
        addMovingCircle(to: view, movingLocation: CLLocationCoordinate2D(latitude: regionToUpdate.center.latitude, longitude: regionToUpdate.center.longitude), radius: radius)
    }
}

添加/移除圆圈Overlay

func addMovingCircle(to view: MKMapView, movingLocation: CLLocationCoordinate2D, radius: Int) {
    for overlay in overlays where overlay.title == "1" {
        view.removeOverlay(overlay)
    }
    let radius: Double = Double(radius) * 1.5
    let movingCircle = MKCircle(center: movingLocation, radius: radius * 1000)
    movingCircle.title = "1"
    view.addOverlay(movingCircle)
}

手势识别代理方法

@objc func addPinBasedOnGesture(_ gestureRecognizer: UIGestureRecognizer) {
    if let mapView = gestureRecognizer.view as? MKMapView {
        mapViewController.updateMovingCircle(regionToUpdate: mapView.region, view: mapView)
    }
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return true
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

Overlay渲染器

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {        
    if let circleOverlay = overlay as? MKCircle {
        let circleRenderer = MKCircleRenderer(overlay: circleOverlay)
        circleRenderer.strokeColor = UIColor(Color.SG.purple)
        circleRenderer.lineWidth = 2
        circleRenderer.lineDashPattern = [2, 5]
        return circleRenderer
    }
    
    return MKOverlayRenderer()
}

解决方案

闪烁的核心原因是频繁移除并重新添加Overlay,导致视图重绘出现断层。以下两种方案均可彻底解决问题:

方案1:复用现有MKCircle,避免重复创建

直接更新已存在的MKCircle属性,而非删除重建:

  1. 在类中保存MKCircle的全局引用:
private var centerCircle: MKCircle?
  1. 修改addMovingCircle方法,复用已有Circle:
func addMovingCircle(to view: MKMapView, movingLocation: CLLocationCoordinate2D, radius: Int) {
    let targetRadius: Double = Double(radius) * 1.5 * 1000
    
    if let existingCircle = centerCircle {
        // 更新中心坐标与半径
        existingCircle.coordinate = movingLocation
        existingCircle.radius = targetRadius
        // 通知地图刷新该Overlay
        view.reloadOverlay(existingCircle)
    } else {
        // 首次创建Circle
        centerCircle = MKCircle(center: movingLocation, radius: targetRadius)
        centerCircle?.title = "1"
        view.addOverlay(centerCircle!)
    }
}
  1. 替换手势监听为地图原生代理方法(覆盖所有地图操作场景):
    在协调器中实现MKMapViewDelegate的区域变化监听:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    mapViewController.updateMovingCircle(regionToUpdate: mapView.region, view: mapView)
}

之后可移除自定义的平移手势,因为地图自身的平移、缩放操作都会触发该代理方法。

方案2:使用UIView直接叠加(更流畅)

跳过Overlay,直接在地图上层添加UIView,通过约束固定中心,随缩放调整大小:

  1. 初始化地图时添加中心圆圈视图:
func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator
    mapView.isUserInteractionEnabled = true
    mapView.showsUserLocation = true
    mapView.showsCompass = false

    // 创建中心圆圈视图
    let centerCircleView = UIView()
    centerCircleView.tag = 1001
    centerCircleView.layer.borderColor = UIColor(Color.SG.purple).cgColor
    centerCircleView.layer.borderWidth = 2
    centerCircleView.layer.cornerRadius = 40
    centerCircleView.layer.borderDashPattern = [2,5] as [NSNumber]
    centerCircleView.translatesAutoresizingMaskIntoConstraints = false
    mapView.addSubview(centerCircleView)
    
    // 约束固定在地图中心
    NSLayoutConstraint.activate([
        centerCircleView.centerXAnchor.constraint(equalTo: mapView.centerXAnchor),
        centerCircleView.centerYAnchor.constraint(equalTo: mapView.centerYAnchor),
        centerCircleView.widthAnchor.constraint(equalToConstant: 80),
        centerCircleView.heightAnchor.constraint(equalToConstant: 80)
    ])
    
    return mapView
}
  1. 在区域变化代理方法中调整视图大小:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    guard let centerCircleView = mapView.viewWithTag(1001) else { return }
    
    // 根据地图缩放比例计算目标尺寸
    let metersPerPoint = mapView.convert(CLLocationDistance(1), toPointTo: mapView.centerCoordinate)
    let targetRadiusMeters = Double(radius) * 1.5 * 1000
    let targetDiameterPoints = targetRadiusMeters * 2 / metersPerPoint
    
    // 更新视图大小与圆角
    centerCircleView.bounds = CGRect(x: 0, y: 0, width: targetDiameterPoints, height: targetDiameterPoints)
    centerCircleView.layer.cornerRadius = targetDiameterPoints / 2
}

该方案无需操作Overlay,直接通过UIView布局实现,完全无闪烁问题。


内容的提问来源于stack exchange,提问作者Bug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:05:07