iOS端如何实现Google Maps标记点标题闪烁效果?
解决iOS Google Maps API标题窗口闪烁问题
这个问题确实戳中了Google Maps iOS SDK的一个小痛点——默认的标记信息窗口是静态快照,没法直接拿到视图引用做动画。不过别担心,我们可以通过自定义信息窗口来实现「标记不动、标题闪烁」的效果,具体步骤如下:
1. 创建自定义信息窗口视图
首先我们需要一个可控制的自定义视图,模拟默认信息窗口的样式,同时暴露标题标签用于动画:
class CustomInfoWindow: UIView { // 用于显示标题的标签,方便我们添加动画 let titleLabel: UILabel = { let label = UILabel() label.textColor = .black label.font = .systemFont(ofSize: 16, weight: .bold) label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } private func setupUI() { // 模拟默认信息窗口的样式 backgroundColor = .white layer.cornerRadius = 8 layer.shadowColor = UIColor.black.cgColor layer.shadowOpacity = 0.2 layer.shadowOffset = CGSize(width: 0, height: 2) layer.shadowRadius = 4 addSubview(titleLabel) NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8), titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12), titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) ]) } }
2. 在地图控制器中实现自定义窗口逻辑
接下来我们需要在地图控制器中管理这个自定义窗口,处理它的显示、位置更新和动画:
class MapViewController: UIViewController, GMSMapViewDelegate { @IBOutlet weak var mapView: GMSMapView! private var markerTracker: GMSMarker? private var customInfoWindow: CustomInfoWindow? override func viewDidLoad() { super.viewDidLoad() mapView.delegate = self // 初始化你的标记(保持原有逻辑不变) let coordinate = CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194) markerTracker = GMSMarker(position: coordinate) markerTracker?.title = Constants.alert let markerImage = UIImage(named: "markerImage")!.withRenderingMode(.alwaysTemplate) markerTracker?.icon = markerImage markerTracker?.map = mapView // 如果需要主动显示标题窗口(无需用户点击) showCustomInfoWindow(for: markerTracker!) } // 显示自定义标题窗口并添加闪烁动画 private func showCustomInfoWindow(for marker: GMSMarker) { // 先移除旧窗口 customInfoWindow?.removeFromSuperview() // 创建新窗口并设置标题 customInfoWindow = CustomInfoWindow() customInfoWindow?.titleLabel.text = marker.title guard let infoWindow = customInfoWindow else { return } view.addSubview(infoWindow) // 计算标记在屏幕上的位置,把窗口放在标记上方 let markerScreenPoint = mapView.projection.point(for: marker.position) let windowSize = infoWindow.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) infoWindow.frame = CGRect( x: markerScreenPoint.x - windowSize.width/2, y: markerScreenPoint.y - windowSize.height - 30, // 30是标记图标的高度,根据你的图标调整 width: windowSize.width, height: windowSize.height ) // 给标题添加闪烁动画 UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse], animations: { infoWindow.titleLabel.alpha = 0.0 }, completion: nil) } // 地图移动时更新窗口位置 func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { guard let marker = markerTracker, let infoWindow = customInfoWindow else { return } let markerScreenPoint = mapView.projection.point(for: marker.position) let windowSize = infoWindow.bounds.size infoWindow.frame = CGRect( x: markerScreenPoint.x - windowSize.width/2, y: markerScreenPoint.y - windowSize.height - 30, width: windowSize.width, height: windowSize.height ) } // 点击地图空白处隐藏窗口 func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { customInfoWindow?.removeFromSuperview() customInfoWindow = nil } // 点击标记时显示自定义窗口(替代默认窗口) func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { if marker == markerTracker { showCustomInfoWindow(for: marker) return true // 返回true表示我们自行处理点击,不显示默认窗口 } return false } }
关键说明
- Google Maps的默认信息窗口是静态快照,无法直接修改或添加动画,所以必须用自定义视图模拟。
- 我们通过
mapView.projection.point(for:)将地理坐标转换为屏幕坐标,确保窗口始终对齐标记位置。 - 动画直接作用在自定义窗口的标题标签上,完全不会影响标记图标的状态。
如果需要停止闪烁动画,可以调用:
customInfoWindow?.titleLabel.layer.removeAllAnimations() customInfoWindow?.titleLabel.alpha = 1.0
内容的提问来源于stack exchange,提问作者Olter
相关产品推荐
相关产品推荐

