修改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
相关产品推荐
相关产品推荐

