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

