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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:24:35