我正在使用适用于iOS的Google Maps API,并希望使用标记群集实用程序。我想出了如何显示聚类标记,但我想自定义标记。有人可以解释如何设置/更改每个标记或聚类标记的图标和标题吗?示例代码非常有用。
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
class MyRenderer: NSObject, GMUClusterRenderer {
var mapView: GMSMapView
var clusterIconGenerator: GMUClusterIconGenerator
var clusterManager: GMUClusterManager
init(mapView: GMSMapView, clusterIconGenerator: GMUClusterIconGenerator, clusterManager: GMUClusterManager) {
self.mapView = mapView
self.clusterIconGenerator = clusterIconGenerator
self.clusterManager = clusterManager
}
func renderClusters(clusters: [GMUCluster]) {
}
func update() {
}
}
这是我到目前为止所拥有的。我不知道如何处理renderClusters和更新函数。
答案 0 :(得分:6)
我设法找到"清洁"解决方案,即使它仍然令人困惑。但它有效!
1)创建.h文件" MarkerManager"
#import <Foundation/Foundation.h>
@import CoreLocation;
#import "GMUClusterItem.h"
#import <GoogleMaps/GoogleMaps.h>
@interface MarkerManager: NSObject
@property (nonatomic) CLLocationCoordinate2D location;
@property (nonatomic, strong) GMSMarker *marker;
@end
2)转到Google-Maps-iOS-Utils文件夹中的GMUDefaultClusterRenderer类,导入MarkerManager.h类查找并更改此方法:
// Returns a marker at final position of |position| with attached |userData|.
// If animated is YES, animates from the closest point from |points|.
- (GMSMarker *)markerWithPosition:(CLLocationCoordinate2D)position
from:(CLLocationCoordinate2D)from
userData:(id)userData
clusterIcon:(UIImage *)clusterIcon
animated:(BOOL)animated {
GMSMarker *marker = [self markerForObject:userData];
CLLocationCoordinate2D initialPosition = animated ? from : position;
marker.position = initialPosition;
marker.userData = userData;
if (clusterIcon != nil) {
marker.icon = clusterIcon;
marker.groundAnchor = CGPointMake(0.5, 0.5);
}
//added
else {
MarkerManager *data = userData;
if(data != nil) {
marker.icon = data.marker.icon;
}
}
//ends here
marker.zIndex = _zIndex;
if ([_delegate respondsToSelector:@selector(renderer:willRenderMarker:)]) {
[_delegate renderer:self willRenderMarker:marker];
}
marker.map = _mapView;
if (animated) {
[CATransaction begin];
[CATransaction setAnimationDuration:kGMUAnimationDuration];
marker.layer.latitude = position.latitude;
marker.layer.longitude = position.longitude;
[CATransaction commit];
}
if ([_delegate respondsToSelector:@selector(renderer:didRenderMarker:)]) {
[_delegate renderer:self didRenderMarker:marker];
}
return marker;
}
3)创建新的swift类,POIItem:
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
@objc var marker: GMSMarker!
init(position: CLLocationCoordinate2D, marker: GMSMarker) {
self.position = position
self.marker = marker
}
}
4)扩展GMUDefaultClusterRenderer类并覆盖markerWithPosition方法:
import Foundation
import UIKit
class CustomMarkers: GMUDefaultClusterRenderer {
var mapView:GMSMapView?
let kGMUAnimationDuration: Double = 0.5
override init(mapView: GMSMapView, clusterIconGenerator iconGenerator: GMUClusterIconGenerator) {
super.init(mapView: mapView, clusterIconGenerator: iconGenerator)
}
func markerWithPosition(position: CLLocationCoordinate2D, from: CLLocationCoordinate2D, userData: AnyObject, clusterIcon: UIImage, animated: Bool) -> GMSMarker {
let initialPosition = animated ? from : position
let marker = GMSMarker(position: initialPosition)
marker.userData! = userData
if clusterIcon.cgImage != nil {
marker.icon = clusterIcon
}
else {
marker.icon = self.getCustomTitleItem(userData: userData)
}
marker.map = mapView
if animated
{
CATransaction.begin()
CAAnimation.init().duration = kGMUAnimationDuration
marker.layer.latitude = position.latitude
marker.layer.longitude = position.longitude
CATransaction.commit()
}
return marker
}
func getCustomTitleItem(userData: AnyObject) -> UIImage {
let item = userData as! POIItem
return item.marker.icon!
}
}
5)在generateClusterItems方法中的MapViewController init POIItem中:
private func generateClusterItems() {
for object in DataManager.sharedInstance.mapItemsArray {
let doubleLat = Double(object.latitude)
let doubleLong = Double(object.longitude)
let latitude = CLLocationDegrees(doubleLat!)
let longitude = CLLocationDegrees(doubleLong!)
let position = CLLocationCoordinate2DMake(latitude, longitude)
let marker = GMSMarker(position: position)
let item = POIItem(position: position, marker: marker)
self.clusterManager.add(item)
item.mapItem = object
}
}
在for循环中你可以调用:
marker.icon = UIImage(named:"YOUR_IMAGE_NAME")
现在,您可以将逻辑设置为具有多个自定义标记。
答案 1 :(得分:5)
我在 Swift 4 上为群集标记找到了一个干净的解决方案,以便为群集中的群集数量使用自定义图像:
class MapClusterIconGenerator: GMUDefaultClusterIconGenerator {
override func icon(forSize size: UInt) -> UIImage {
let image = textToImage(drawText: String(size) as NSString,
inImage: UIImage(named: "cluster")!,
font: UIFont.systemFont(ofSize: 12))
return image
}
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=[
NSAttributedStringKey.font: font,
NSAttributedStringKey.paragraphStyle: textStyle,
NSAttributedStringKey.foregroundColor: textColor]
// vertically center (depending on font)
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!
}
}
比集群管理器的设置:
private func setupClustering() {
guard let mapView = self.mapView else { return }
let iconGenerator = MapClusterIconGenerator()
let renderer = MapClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
}
我还使用了自定义群集渲染器MapClusterRenderer
。
答案 2 :(得分:1)
如果您将Google-Maps-iOS-Utils源文件包含在您的项目中,则会有一个&#34;脏&#34;改变标记图标的方法。
不幸的是,没有公共方法来设置自定义图标,但您可以在源文件中更改它。
在Google Map Utils / Clustering / View / GMUDefaultClusterRenderer.m中
- (void)renderCluster:(id<GMUCluster>)cluster animated:(BOOL)animated {
...
GMSMarker *marker = [self markerWithPosition:item.position
from:fromPosition
userData:item
clusterIcon:[UIImage imageNamed:@"YOUR_CUSTOM_ICON"]
animated:shouldAnimate];
...
}
您可以设置集群管理器(Swift)
private func setupClusterManager() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
renderer: renderer)
}
答案 3 :(得分:1)
在 Swift 4.2 中:
您可以使用GNUClusterRendererDelegate
:
将此扩展名添加到您的控制器中,并确保您的控制器是GMUClusterRendererDelegate
的委托:
willRenderMarker
每次将要渲染的标记(集群标记和clusterItemMarker都将调用),因此您可以通过简单的if对其进行检查)。因此,可以在向用户显示
extension YourController: GMUClusterRendererDelegate {
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
// if your marker is pointy you can change groundAnchor
marker.groundAnchor = CGPoint(x: 0.5, y: 1)
if let markerData = (marker.userData as? PersonMarker) {
let icon = markerData.imageURL
marker.iconView = CustomMarkerView(forUrl: url)
}
}
}
PersonMarker是您的标记类,它是NSObject
和GMUClusterItem
的子类:(您可以使用默认的类GMUClusterItem
,但如果需要其他属性,可以将其子类化)
class PersonMarker: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var imageURL : String?
var name: String?
var userdId: String?
var lastSeen: String?
init(position: CLLocationCoordinate2D, url: String?, name: String?, userId: String?, lastSeen: String?) {
self.position = position
self.imageURL = url
self.name = name
self.userdId = userId
self.lastSeen = lastSeen
}
}
您可以像这样将PersonMarker
添加到GMUClusterManager
中:
let position = CLLocationCoordinate2D(latitude: item.latitude!, longitude: item.longitute!)
let person = PersonMarker(position: position, url: item.user?.avaterUrl, name: item.user?.name, userId: item.user?.userId, lastSeen: item.lastUpdate)
clusterManager.add(person)
答案 4 :(得分:0)
本教程介绍了如何使用标记聚类对多个地图标记进行分组
1)
class HFDashBordVC: UIViewController,GMUClusterManagerDelegate,GMSMapViewDelegate{
private var clusterManager: GMUClusterManager!
var cameraPosition = GMSCameraPosition()
var markerPin = GMSMarker()
var currentLocationCircle = GMSCircle()
var locationManager = CLLocationManager()
var currentLocation: CLLocation?
var zoomLevel: Float = 18.0
@IBOutlet var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
setupClustering()
}
func setupClustering() {
let iconGenerator = CustomClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = CustomRendererMarkers(mapView: mapView,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
renderer: renderer)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
private func setClusterParkingPin() {
clusterManager.clearItems()
for object in AppUtilites.sharedInstance.safeParkingArray {
let model = object as! AMParkModel
let lat = Double(model.latitude)!
let lng = Double(model.longitude)!
let position = CLLocationCoordinate2DMake(lat, lng)
let marker = GMSMarker(position: position)
marker.icon = HFAsset.ic_p_pin_orange.image
let userData = model
let item = POIItem(position: position, marker: marker, userData: userData)
self.clusterManager.add(item)
self.setCurrentPin(zoomLevel: 14.0)
}
}
2)
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var userData: AnyObject!
@objc var marker: GMSMarker!
init(position: CLLocationCoordinate2D, marker: GMSMarker, userData: AnyObject) {
self.position = position
self.marker = marker
self.userData = userData
}
}
3)
class CustomRendererMarkers: GMUDefaultClusterRenderer {
var mapView:GMSMapView?
let kGMUAnimationDuration: Double = 0.5
override init(mapView: GMSMapView, clusterIconGenerator iconGenerator: GMUClusterIconGenerator) {
super.init(mapView: mapView, clusterIconGenerator: iconGenerator)
}
func markerWithPosition(position: CLLocationCoordinate2D, from: CLLocationCoordinate2D, userData: AnyObject, clusterIcon: UIImage, animated: Bool) -> GMSMarker {
let initialPosition = animated ? from : position
let marker = GMSMarker(position: initialPosition)
marker.userData! = userData
if clusterIcon.cgImage != nil {
marker.icon = clusterIcon
}
else {
marker.icon = self.getCustomTitleItem(userData: userData)
}
marker.map = mapView
if animated
{
CATransaction.begin()
CAAnimation.init().duration = kGMUAnimationDuration
marker.layer.latitude = position.latitude
marker.layer.longitude = position.longitude
CATransaction.commit()
}
return marker
}
func getCustomTitleItem(userData: AnyObject) -> UIImage {
let item = userData as! POIItem
return item.marker.icon!
}
}
4)
class CustomClusterIconGenerator: GMUDefaultClusterIconGenerator {
override func icon(forSize size: UInt) -> UIImage {
let image = textToImage(drawText: (String(size) as NSString) as String,
inImage: HFAsset.ic_parking_clustering.image,
font: UIFont.init(name: "Montserrat-Medium", size: 12.0)!)
return image
}
private func textToImage(drawText text: String, inImage image: UIImage, font: UIFont) -> UIImage {
var pinCount = text
if Int(text)! > 9999 {
pinCount = "+9999"
}
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.white
let attributes=[
NSAttributedStringKey.font: font,
NSAttributedStringKey.paragraphStyle: textStyle,
NSAttributedStringKey.foregroundColor: textColor,
NSAttributedStringKey.backgroundColor: appThemeColor]
// vertically center (depending on font)
let textH = font.lineHeight
let textY = (image.size.height-textH)/3
let textRect = CGRect(x: 0, y: textY, width: image.size.width, height: textH)
pinCount.draw(in: textRect.integral, withAttributes: attributes)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
}
6)
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if markerPin == marker {
return true
}
if ((marker.userData as? GMUStaticCluster) != nil) { // Cluster Pin tap
UIView.animate(withDuration: 0.5, delay: 0.2, options: [.curveEaseOut],
animations: {
let newCamera = GMSCameraPosition.camera(withTarget: marker.position,
zoom: self.mapView.camera.zoom + 0.8)
let update = GMSCameraUpdate.setCamera(newCamera)
self.mapView.animate(with: update)
}, completion: {
finished in
})
}
if let poiItem = marker.userData as? POIItem { // Cluster Parking Pin tap
let Detail = self.storyboard?.instantiateViewController(withIdentifier: "HFParkingFullDetailsVC")as! HFParkingFullDetailsVC
Detail.parkModel = (poiItem.userData as? AMParkModel)!
self.navigationController?.pushViewController(Detail, animated: true)
}
答案 5 :(得分:0)
如果只需要更改图标或颜色,则可以在初始化GMUDefaultClusterIconGenerator时添加具有多种颜色/图像的存储桶(如果只需要一种颜色,则可以在下面的情况中仅添加一种)。我使用了大量(高于群集项目的最大数量),以便所有群集都具有相同的颜色。要使用多种颜色,您可以添加多种存储桶和多种颜色。
let iconGenerator = GMUDefaultClusterIconGenerator.init(buckets: [99999], backgroundColors: [UIColor.red])
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: googleMapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: googleMapView, algorithm: algorithm, renderer: renderer)
clusterManager.setDelegate(self, mapDelegate: self)
要将图像用作群集背景,可以为存储桶提供一组backgroundImages:
let iconGenerator = GMUDefaultClusterIconGenerator.init(buckets: [99999], backgroundImages: [UIImage(named: "YOUR_IMAGE_HERE")!])