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

修改tracksViewChanges属性时GMSMarker在地图上闪烁问题求助

解决Google Maps自定义GMSMarker更新闪烁问题

核心问题分析

每次单独切换tracksViewChanges的true/false会触发标记视图的多次重建与重绘,这是导致闪烁的根本原因;同时大量标记的动态渲染会直接拉低帧率。


具体解决方法

1. 批量切换tracksViewChanges,减少重绘次数

不要逐个标记开启/关闭视图追踪,改为统一批量操作,减少系统的重绘触发频率:

func updatePinOverlap(mapView: GMSMapView) {
    // 统一开启所有标记的视图追踪
    discoveryMarker.forEach { $0.tracksViewChanges = true }
    
    // 批量执行标记状态更新
    discoveryMarker.forEach { marker in
        marker.applyOverlapping(pinType: model.pinType ?? .medium,
                                index: model.zIndex ?? 0)
    }
    
    // 统一关闭所有标记的视图追踪
    discoveryMarker.forEach { $0.tracksViewChanges = false }
}

2. 预渲染标记图片,避免动态视图修改

提前生成所有状态(显示/隐藏标题、不同PinType)的静态图片,更新时直接设置marker.icon,完全不需要切换tracksViewChanges:

  • 第一步:预缓存不同状态的图片
// 按PinType和标题显示状态缓存图片
let cachedPinImages: [PinType: [Bool: UIImage]] = [
    .medium: [
        true: UIImage(named: "medium_pin_with_title")!,
        false: UIImage(named: "medium_pin_no_title")!
    ],
    .small: [
        true: UIImage(named: "small_pin_with_title")!,
        false: UIImage(named: "small_pin_no_title")!
    ]
    // 扩展其他PinType
]
  • 第二步:直接更新标记的icon
func updatePinOverlap(mapView: GMSMapView) {
    discoveryMarker.forEach { marker in
        // 计算当前标记是否需要显示标题(你的重叠判断逻辑)
        let shouldShowTitle = calculateShouldShowTitle(for: marker)
        // 获取对应状态的图片
        let targetImage = cachedPinImages[model.pinType ?? .medium]![shouldShowTitle]!
        
        // 直接设置icon和zIndex,无需修改tracksViewChanges
        marker.icon = targetImage
        marker.zIndex = model.zIndex ?? 0
    }
}

这种方式是性能最优的,因为修改icon属于GMSMarker的轻量更新,不会触发视图重建。

3. 跳过无意义的更新,减少不必要操作

给GMSMarker扩展存储当前状态的属性,更新前先判断状态是否变化,仅在需要时执行更新:

  • 扩展GMSMarker存储状态
extension GMSMarker {
    private struct AssociatedKeys {
        static var pinType = "pinType"
        static var zIndex = "zIndex"
        static var showTitle = "showTitle"
    }
    
    var storedPinType: PinType? {
        get { objc_getAssociatedObject(self, &AssociatedKeys.pinType) as? PinType }
        set { objc_setAssociatedObject(self, &AssociatedKeys.pinType, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
    
    var storedZIndex: Int? {
        get { objc_getAssociatedObject(self, &AssociatedKeys.zIndex) as? Int }
        set { objc_setAssociatedObject(self, &AssociatedKeys.zIndex, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
    
    var storedShowTitle: Bool? {
        get { objc_getAssociatedObject(self, &AssociatedKeys.showTitle) as? Bool }
        set { objc_setAssociatedObject(self, &AssociatedKeys.showTitle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}
  • 更新时判断状态是否变化
func updatePinOverlap(mapView: GMSMapView) {
    discoveryMarker.forEach { marker in
        let newPinType = model.pinType ?? .medium
        let newZIndex = model.zIndex ?? 0
        let shouldShowTitle = calculateShouldShowTitle(for: marker)
        
        // 状态未变化则跳过更新
        guard marker.storedPinType != newPinType || 
              marker.storedZIndex != newZIndex || 
              marker.storedShowTitle != shouldShowTitle else {
            return
        }
        
        marker.tracksViewChanges = true
        marker.applyOverlapping(pinType: newPinType, index: newZIndex)
        // 更新存储的状态
        marker.storedPinType = newPinType
        marker.storedZIndex = newZIndex
        marker.storedShowTitle = shouldShowTitle
        marker.tracksViewChanges = false
    }
}

4. 用聚类管理大量标记

如果标记数量超过数百个,建议使用Google官方的GMSClusterManager,它会自动在地图视口内聚类标记,仅渲染可见的标记,从根源上减少需要更新的标记数量,同时提升整体性能。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:35:55