如何实现Google Maps中点击集群图标切换图标
解决Google Maps集群图标点击切换问题
我明白你的问题:单个标记点击能切换图标,但集群图标点击后毫无反应。核心原因是你没有跟踪集群标记的选中状态,也没有在点击时更新集群图标的逻辑。下面是一步步的解决方案:
1. 扩展CustomMarker以支持集群选中状态
首先,确保你的CustomMarker能记录集群的选中状态,复用现有的isSelected属性即可(通过markerType区分是单个标记还是集群):
class CustomMarker: GMSMarker { enum MarkerType { case clusterType case itemType } var markerType: MarkerType = .itemType var markerCount: Int = 0 var propertyListModal: PeropertyListModel? var isSelected: Bool = false // 单个标记和集群共用这个状态 }
2. 修改ClusterIconGenerator支持选中状态的图标生成
让图标生成器能根据选中状态返回不同的基础图片(比如选中时用clusterMarkerSelected,未选中用clusterMarker):
class ClusterIconGenerator: GMUDefaultClusterIconGenerator { // 新增方法:根据选中状态生成图标 func icon(forSize size: UInt, isSelected: Bool) -> UIImage { let baseImageName = isSelected ? "clusterMarkerSelected" : "clusterMarker" guard let baseImage = UIImage(named: baseImageName) else { return super.icon(forSize: size) // fallback到默认逻辑 } return textToImage(drawText: String(size) as NSString, inImage: baseImage, font: UIFont(name: "Futura-Medium", size: 12.0) ?? UIFont.systemFont(ofSize: 12)) } // 保留原有方法供默认调用 override func icon(forSize size: UInt) -> UIImage { return icon(forSize: size, isSelected: false) } // 原有textToImage方法不变 private func textToImage(drawText text: NSString, inImage image: UIImage, font: UIFont) -> UIImage { UIGraphicsBeginImageContext(image.size) image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)) let textStyle = NSMutableParagraphStyle() textStyle.alignment = NSTextAlignment.center let textColor = UIColor.black let attributes=[ NSAttributedString.Key.font: font, NSAttributedString.Key.paragraphStyle: textStyle, NSAttributedString.Key.foregroundColor: textColor] let textH = font.lineHeight let textY = (image.size.height-textH)/2 let textRect = CGRect(x: 0, y: textY, width: image.size.width, height: textH) text.draw(in: textRect.integral, withAttributes: attributes) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result! } }
3. 在ViewController中跟踪选中的集群标记
添加一个变量来记录当前选中的集群标记,方便后续重置状态:
class SearchViewController: UIViewController { private var selectedClusterMarker: CustomMarker? // 你的其他现有变量... }
4. 更新Renderer代理方法处理集群图标
修改renderer(_:markerFor:)初始化集群标记的状态,同时在renderer(_:didRenderMarker:)中根据选中状态生成集群图标:
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? { let marker = CustomMarker() if let itemMarker = object as? GMUCluster { marker.markerType = .clusterType marker.markerCount = itemMarker.items.count // 初始化选中状态:如果是之前选中的集群,设置为true marker.isSelected = (selectedClusterMarker?.markerCount == itemMarker.items.count && selectedClusterMarker?.position == itemMarker.position) } else if let itemMarker = object as? POIItem { marker.markerType = .itemType itemMarker.marker = marker marker.propertyListModal = itemMarker.propertyListModal marker.isSelected = false } return marker } func renderer(_ renderer: GMUClusterRenderer, didRenderMarker marker: GMSMarker) { if let customMarker = marker as? CustomMarker { if customMarker.markerType == .itemType { // 原有单个标记的处理逻辑不变 let propertyMarker = createMarkerView(isSelected: customMarker.isSelected) propertyMarker.markerPriceLabel.text = customMarker.propertyListModal?.propertyPrice marker.icon = propertyMarker.asImage() marker.groundAnchor = CGPoint(x: 0.5, y: 1) } else if customMarker.markerType == .clusterType { // 新增集群图标渲染逻辑 let iconGenerator = ClusterIconGenerator() let clusterIcon = iconGenerator.icon(forSize: UInt(customMarker.markerCount), isSelected: customMarker.isSelected) marker.icon = clusterIcon marker.groundAnchor = CGPoint(x: 0.5, y: 0.5) // 集群图标通常居中显示 } } }
5. 修改集群点击代理方法,实现图标切换
在clusterManager(_:didTap:)中,处理集群点击时的状态切换和图标更新:
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool { // 1. 重置之前选中的集群状态 if let previousClusterMarker = selectedClusterMarker { previousClusterMarker.isSelected = false let iconGenerator = ClusterIconGenerator() let clusterIcon = iconGenerator.icon(forSize: UInt(previousClusterMarker.markerCount), isSelected: false) previousClusterMarker.icon = clusterIcon } // 2. 获取当前点击的集群对应的Marker let renderer = clusterManager.renderer as! GMUDefaultClusterRenderer guard let currentClusterMarker = renderer.marker(for: cluster) as? CustomMarker else { // 执行原有逻辑(如果没找到标记) let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: propertyMapView.camera.zoom) propertyMapView.moveCamera(GMSCameraUpdate.setCamera(newCamera)) isClusterTapped = true clusterPropertyList = cluster.items.compactMap { ($0 as? POIItem)?.propertyListModal } self.propertyMapView.selectedMarker = nil self.propertyListContainerForMap.isHidden = false self.propertyViewForMapCollectionView.reloadData() return true } // 3. 切换当前集群的选中状态并更新图标 currentClusterMarker.isSelected = !currentClusterMarker.isSelected let iconGenerator = ClusterIconGenerator() let clusterIcon = iconGenerator.icon(forSize: UInt(currentClusterMarker.markerCount), isSelected: currentClusterMarker.isSelected) currentClusterMarker.icon = clusterIcon // 4. 更新选中集群的记录 selectedClusterMarker = currentClusterMarker.isSelected ? currentClusterMarker : nil // 5. 执行原有业务逻辑 let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: propertyMapView.camera.zoom) propertyMapView.moveCamera(GMSCameraUpdate.setCamera(newCamera)) isClusterTapped = true clusterPropertyList = cluster.items.compactMap { ($0 as? POIItem)?.propertyListModal } self.propertyMapView.selectedMarker = nil self.propertyListContainerForMap.isHidden = false self.propertyViewForMapCollectionView.reloadData() return true }
6. 点击单个标记时重置集群选中状态
在单个标记的点击代理方法中,添加重置集群选中状态的逻辑,避免状态冲突:
func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool { // 重置选中的集群 if let previousClusterMarker = selectedClusterMarker { previousClusterMarker.isSelected = false let iconGenerator = ClusterIconGenerator() let clusterIcon = iconGenerator.icon(forSize: UInt(previousClusterMarker.markerCount), isSelected: false) previousClusterMarker.icon = clusterIcon selectedClusterMarker = nil } // 原有单个标记的处理逻辑不变 let newCamera = GMSCameraPosition.camera(withTarget: clusterItem.position, zoom: propertyMapView.camera.zoom) propertyMapView.moveCamera(GMSCameraUpdate.setCamera(newCamera)) if let previousMarker = propertyMapView.selectedMarker as? CustomMarker{ previousMarker.isSelected = !previousMarker.isSelected let propertyMarker = createMarkerView(isSelected: previousMarker.isSelected) propertyMarker.markerPriceLabel.text = previousMarker.propertyListModal?.propertyPrice previousMarker.icon = propertyMarker.asImage() } let item = clusterItem as? POIItem if let tappedMarker = item?.marker as? CustomMarker{ tappedMarker.isSelected = !tappedMarker.isSelected let propertyMarker = createMarkerView(isSelected: tappedMarker.isSelected) propertyMarker.markerPriceLabel.text = tappedMarker.propertyListModal?.propertyPrice tappedMarker.icon = propertyMarker.asImage() propertyMapView.selectedMarker = tappedMarker } isClusterTapped = false self.propertyListContainerForMap.isHidden = false self.propertyViewForMapCollectionView.reloadData() return true }
这样修改后,点击集群图标时就会切换选中状态并更新图标,同时点击单个标记时会自动重置集群的选中状态,确保交互逻辑一致。
内容的提问来源于stack exchange,提问作者Manish
相关产品推荐
相关产品推荐

