Reputation: 171
I am using Google Maps SDK in my iOS app. I am populating the map using the clustering methods.
I have set custom images for the different clustering buckets ex. 10,20...
The individual markers however have the default (google maps red marker icon).
I would like a custom icon for clustering and a different one for single markers.
However inside the methods that render the Cluster that add markers, if you set the marker icons it changes all of the images not just singles.
How do I set different icons for singles and clusters?
this adds the items to clusterManager
id<GMUClusterItem> item =
[[POIItem alloc] initWithPosition:CLLocationCoordinate2DMake([bay.latitude doubleValue], [bay.longitude doubleValue]) name:bay.name status:bay.marker_status];
[clusterManager addItem:item];
Here I add the icons for the cluster buckets
- (id<GMUClusterIconGenerator>)iconGeneratorWithImages {
return [[GMUDefaultClusterIconGenerator alloc] initWithBuckets:@[ @10, @50, @100, @200, @1000 ]
backgroundImages:@[
[UIImage imageNamed:@"big_parking_pin_img"],
[UIImage imageNamed:@"big_parking_pin_img"],
[UIImage imageNamed:@"big_parking_pin_img"],
[UIImage imageNamed:@"big_parking_pin_img"],
[UIImage imageNamed:@"big_parking_pin_img"]
]];
}
This is where the google cluster class adds markers
- (void)renderCluster:(id<GMUCluster>)cluster animated:(BOOL)animated {
float zoom = _mapView.camera.zoom;
if ([self shouldRenderAsCluster:cluster atZoom:zoom]) {
CLLocationCoordinate2D fromPosition;
if (animated) {
id<GMUCluster> fromCluster =
[self overlappingClusterForCluster:cluster itemMap:_itemToOldClusterMap];
animated = fromCluster != nil;
fromPosition = fromCluster.position;
}
UIImage *icon = [_clusterIconGenerator iconForSize:cluster.count];
GMSMarker *marker = [self markerWithPosition:cluster.position
from:fromPosition
userData:cluster
clusterIcon:icon
animated:animated];
[_markers addObject:marker];
} else {
for (id<GMUClusterItem> item in cluster.items) {
CLLocationCoordinate2D fromPosition;
BOOL shouldAnimate = animated;
if (shouldAnimate) {
GMUWrappingDictionaryKey *key = [[GMUWrappingDictionaryKey alloc] initWithObject:item];
id<GMUCluster> fromCluster = [_itemToOldClusterMap objectForKey:key];
shouldAnimate = fromCluster != nil;
fromPosition = fromCluster.position;
}
GMSMarker *marker = [self markerWithPosition:item.position
from:fromPosition
userData:item
clusterIcon:nil
animated:shouldAnimate];
[_markers addObject:marker];
[_renderedClusterItems addObject:item];
}
}
[_renderedClusters addObject:cluster];
}
// 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 {
CLLocationCoordinate2D initialPosition = animated ? from : position;
GMSMarker *marker = [GMSMarker markerWithPosition:initialPosition];
marker.userData = userData;
if (clusterIcon != nil) {
marker.icon = clusterIcon;
marker.groundAnchor = CGPointMake(0.5, 0.5);
}
marker.map = _mapView;
if (animated) {
[CATransaction begin];
[CATransaction setAnimationDuration:kGMUAnimationDuration];
marker.layer.latitude = position.latitude;
marker.layer.longitude = position.longitude;
[CATransaction commit];
}
return marker;
}
Upvotes: 1
Views: 1582
Reputation: 11
I had the similar problem 2 days ago and I just found the solution. Hope it will be useful for you. For example you have a mapView and you set a delegate to it in right place:
[self.mapView setDelegate:self];
Then you need to implement the optional method from GMSMapViewDelegate protocol:
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position {
[self performSelector:@selector(updateMarkers) withObject:nil afterDelay:0.2];
}
I use delay 0.2 seconds, because markers wouldn't update their icons if you'll use smaller value. The next step is implement method for updating icons:
-(void) updateMarkers {
// "mapView" property in your self.mapView has type GMSVectorMapView,
//and it is hidden, so you can't get like self.mapView.mapView
id vectorMap = [self.mapView valueForKey:@"mapView"];
// "accessibilityItems" - property that have all items in visible part of map.
NSMutableArray* GMSMarkersArray = [vectorMap mutableArrayValueForKey:@"accessibilityItems"];
// Very often you'll get object of GMSPointOfInteretUIItem class, and you don't need it =)
NSMutableArray *discardedItems = [NSMutableArray array];
for (id item in GMSMarkersArray) {
if (![item isKindOfClass:[GMSMarker class]])
[discardedItems addObject:item];
}
[GMSMarkersArray removeObjectsInArray:discardedItems];
// If marker don't have icon image, he use default red pin, but property is still have nil-value ...
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"icon = nil"];
NSArray* singleMarkers = [GMSMarkersArray filteredArrayUsingPredicate:predicate];
// ... and here you can setup any icon you want, for all singles markers in visible part of map.
for(GMSMarker* marker in singleMarkers) {
marker.icon = [UIImage imageNamed:@"yourIcon.png"];
}
}
Also if you create your own marker and add it to cluster, you can get it from userData property of GMSMarker object in last loop. And for example you have there your custom marker with icon you want, just change last loop for something like:
for(GMSMarker* marker in singleMarkers) {
YourMarkerClass* yourMaker = marker.userData;
marker.icon = yourMaker.icon;
}
Sorry for possible mistakes and ask the questions if you don't understand something =)
Upvotes: 1