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

iOS Swift中如何解析自定义标记图标并实现地图聚类功能?

嘿,针对你在iOS Swift环境下解析自定义标记图标并实现地图聚类的需求,结合你给出的Venue模型,我整理了一套完整的实现方案,咱们一步步来:


一、优化自定义标记图标解析逻辑

首先你的Venue模型里直接存储GMSMarker不太合理(Marker属于视图层对象,模型应该专注存储数据),我们调整模型来适配JSON解析和后续聚类需求:

import GoogleMaps
import SwiftyJSON // 假设你用SwiftyJSON处理JSON解析

class Venue: NSObject, GMUClusterItem {
    let name: String?
    let locationName: String?
    let position: CLLocationCoordinate2D
    let markerIcon: UIImage? // 存储自定义图标(数据层)

    init(name: String, locationName: String?, position: CLLocationCoordinate2D, markerIcon: UIImage?) {
        self.name = name
        self.locationName = locationName
        self.position = position
        self.markerIcon = markerIcon
        super.init()
    }

    var subtitle: String? {
        return locationName
    }

    // 生成对应GMSMarker的方法(视图层逻辑抽离)
    func createMarker() -> GMSMarker {
        let marker = GMSMarker(position: position)
        marker.title = name
        marker.subtitle = locationName
        marker.icon = markerIcon
        return marker
    }

    // 从JSON解析Venue的核心方法
    class func from(json: JSON) -> Venue? {
        guard let name = json["name"].string,
              let lat = json["lat"].double,
              let lng = json["lng"].double else {
            return nil
        }
        
        let locationName = json["locationName"].string
        var markerIcon: UIImage?
        
        // 情况1:JSON返回Assets中的图标名称
        if let iconName = json["iconName"].string {
            markerIcon = UIImage(named: iconName)
        }
        // 情况2:JSON返回图标网络URL(需异步下载,示例用同步逻辑,实际建议用SDWebImage等库异步处理)
        else if let iconUrlStr = json["iconUrl"].string, let url = URL(string: iconUrlStr) {
            if let data = try? Data(contentsOf: url) {
                markerIcon = UIImage(data: data)
            }
        }

        let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        return Venue(name: name, locationName: locationName, position: position, markerIcon: markerIcon)
    }
}

二、实现地图聚类功能(基于Google Maps Utils)

我们使用Google官方的GoogleMapsUtils库来实现聚类,步骤如下:

1. 配置依赖

确保你的Podfile中包含以下依赖,执行pod install:

pod 'GoogleMaps'
pod 'GoogleMapsUtils'

2. 初始化地图与聚类管理器

在你的地图视图控制器中完成基础配置:

import GoogleMaps
import GoogleMapsUtils

class MapViewController: UIViewController {
    private var mapView: GMSMapView!
    private var clusterManager: GMUClusterManager!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupMapView()
        setupClusterManager()
        loadVenuesFromLocalJSON()
    }

    // 初始化地图视图
    private func setupMapView() {
        let initialCamera = GMSCameraPosition.camera(withLatitude: 37.7749, longitude: -122.4194, zoom: 10)
        mapView = GMSMapView.map(withFrame: view.bounds, camera: initialCamera)
        view.addSubview(mapView)
    }

    // 初始化聚类管理器
    private func setupClusterManager() {
        // 聚类算法(非层级距离聚类,适合大多数场景)
        let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
        // 自定义聚类图标生成器(也可以用默认)
        let iconGenerator = GMUDefaultClusterIconGenerator()
        // 自定义渲染器(控制单个Marker和聚类Marker的样式)
        let renderer = CustomClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
        
        clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
        // 设置代理处理点击事件
        clusterManager.setDelegate(self, mapDelegate: self)
    }

    // 加载本地JSON并解析Venue
    private func loadVenuesFromLocalJSON() {
        guard let jsonPath = Bundle.main.path(forResource: "venues", ofType: "json"),
              let jsonData = try? Data(contentsOf: URL(fileURLWithPath: jsonPath)),
              let jsonArray = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] else {
            print("JSON加载失败")
            return
        }

        jsonArray.forEach { item in
            let json = JSON(item)
            if let venue = Venue.from(json: json) {
                clusterManager.add(venue)
            }
        }

        // 触发聚类计算
        clusterManager.cluster()
    }
}

3. 自定义聚类渲染器

创建CustomClusterRenderer来控制单个标记和聚类标记的显示样式:

class CustomClusterRenderer: GMUDefaultClusterRenderer {
    // 自定义聚类标记样式
    override func renderCluster(_ cluster: GMUCluster, marker: GMSMarker) {
        marker.icon = UIImage(named: "cluster-icon") // 替换成你的自定义聚类图标
        marker.title = "\(cluster.count)个地点"
        marker.snippet = "点击查看详情"
    }

    // 自定义单个Venue标记样式
    override func renderMarker(_ marker: GMSMarker, for item: GMUClusterItem) {
        guard let venue = item as? Venue else { return }
        marker.icon = venue.markerIcon
        marker.title = venue.name
        marker.subtitle = venue.locationName
    }
}

4. 实现聚类点击事件代理

扩展视图控制器处理聚类和单个标记的点击:

extension MapViewController: GMUClusterManagerDelegate, GMSMapViewDelegate {
    // 点击聚类标记的逻辑
    func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
        // 点击聚类后放大地图
        let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1.5)
        mapView.animate(to: newCamera)
        return false
    }

    // 点击单个Venue标记的逻辑
    func clusterManager(_ clusterManager: GMUClusterManager, didTap item: GMUClusterItem) -> Bool {
        guard let venue = item as? Venue else { return false }
        print("点击了地点:\(venue.name ?? "未知地点")")
        // 这里可以添加弹窗跳转等逻辑
        return true
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:43:52