Why GMUClusterManager is not showing on iOS? - ios

I try to use SwiftUI view using the UIViewRepresentable pattern, the GoogleMaps background works but the ClusterManager doesn't
show anything. Is there something wrong?
I try to generate 10000 markers like in the sample of Google Maps documentation, but instead of using UIKit with "UIViewController" I try to use SwiftUI with UIViewRepresentable just like they did there. It works if I just use markers but if I try to use ClusterManager it doesn't work.
import SwiftUI
import GoogleMaps
import GoogleMapsUtils
struct GoogleMapsView: UIViewRepresentable {
let kClusterItemCount = 10000
let kCameraLatitude = 48.860294
let kCameraLongitude = 2.338629
func makeUIView(context: Self.Context) -> GMSMapView {
print("-- GoogleMapsView -- : Making UiView MapView")
let camera = GMSCameraPosition.camera(withLatitude: kCameraLatitude,
longitude: kCameraLongitude, zoom: 10)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
return mapView
}
func makeCoordinator() -> MapCoordinator {
print("-- GoogleMapsView -- : Making Coordinator")
return MapCoordinator(owner: self)
}
func updateUIView(_ mapView: GMSMapView, context: Context) {
print("-- GoogleMapsView -- : Updating UI View")
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
let clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
generateClusterItems(clusterManager: clusterManager)
clusterManager.cluster()
}
private func generateClusterItems(clusterManager : GMUClusterManager) {
let extent = 0.2
for _ in 1...kClusterItemCount {
let lat = kCameraLatitude + extent * randomScale()
let lng = kCameraLongitude + extent * randomScale()
let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
let marker = GMSMarker(position: position)
clusterManager.add(marker)
}
}
/// Returns a random value between -1.0 and 1.0.
private func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
}
I just got this.

You are creating clusterManager inside updateUIView but not saving it anywhere, so it’s immediately discarded. In the Google sample code, they save clusterManager as a property of the view controller; you don’t have a view controller, so I’d recommend moving clusterManager and related code into your MapCoordinator class. Coordinators are kept around for the lifetime of your view and can be accessed from makeUIView and updateUIView using context.coordinator.

Related

Using Google Places API to find nearby places BY TYPE (parks) for iOS SwiftUI

I'm pretty new to Swift/iOS app dev. so far, so I'm struggling to figure this out. Basically, I'm trying to make it so when the app opens (first screen is the map), it automatically finds nearby places that are only parks around the user's current location and have these locations annotated with markers on a map (Google Maps) using Google Places API for iOS using updated SwiftUI/Swift 5.0: Using no storyboards!. Table I types, in this case, parks: https://developers.google.com/places/web-service/supported_types
So far, this is the code I have... It uses GMSPlaceLikelihood of places nearby the user's location. This is more so an example of what I want to achieve, however using Google Places API nearby search instead so I can show only parks. Image of app running:
Image
(The place's found from nearby are then listed in a table as shown on the image. This list is just for show)
Thanks in advance for any advice/help.
GoogleMapsView.swift:
#ObservedObject var locationManager = LocationManager()
#ObservedObject var place = PlacesManager()
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 14)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.isMyLocationEnabled = true
mapView.settings.rotateGestures = false
mapView.settings.tiltGestures = false
mapView.isIndoorEnabled = false
mapView.isTrafficEnabled = false
mapView.isBuildingsEnabled = false
mapView.settings.myLocationButton = true
place.currentPlacesList(completion: { placeLikelihoodList in
if let placeLikelihoodList = placeLikelihoodList {
print("total places: \(placeLikelihoodList.count)")
for likelihood in placeLikelihoodList {
let place = likelihood.place
let position = CLLocationCoordinate2D(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
let marker = GMSMarker(position: position)
marker.title = place.name
marker.map = mapView
}
}
})
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Context) {
// let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: zoom)
// mapView.camera = camera
mapView.animate(toLocation: CLLocationCoordinate2D(latitude: locationManager.latitude, longitude: locationManager.longitude))
}
PlacesManager.swift:
class PlacesManager: NSObject, ObservableObject {
private var placesClient = GMSPlacesClient.shared()
#Published var places = [GMSPlaceLikelihood]()
override init() {
super.init()
currentPlacesList { (places) in
guard let places = places else {
return
}
self.places = places
}
}
func currentPlacesList(completion: #escaping (([GMSPlaceLikelihood]?) -> Void)) {
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) | UInt(GMSPlaceField.types.rawValue) | UInt(GMSPlaceField.coordinate.rawValue))!
placesClient.findPlaceLikelihoodsFromCurrentLocation(withPlaceFields: fields, callback: {
(placeLikelihoodList: Array<GMSPlaceLikelihood>?, error: Error?) in
if let error = error {
print("An error occurred: \(error.localizedDescription)")
return
}
if let placeLikelihoodList = placeLikelihoodList {
for likelihood in placeLikelihoodList {
let place = likelihood.place
}
completion(placeLikelihoodList)
}
})
}
ContentView.swift:
var body: some View {
VStack {
GoogleMapsView()
.edgesIgnoringSafeArea(.top)
.frame(height: 400)
PlacesList()
}
.offset(y: 100)
}
https://developers.google.com/places/web-service/search
I suggest you visit this page, it describes the API in detail and shows examples how to call it with different parameters.
After you get the response (I suggest getting it in json format and using SwiftyJSON cocoa pod to parse it) populate the table.

MapKit: Route not being displayed between two annotations

Im trying to display a route between two annotations.
The annotations and the region work fine but the route won't show up and I have no idea why
It looks like the route is not being rendered at all.
I'm sure that the route exists because I tried to print it and it is in the directionResponse.routes
Any suggestions?
I'm using SwiftUI
Then this is included in a parent view.
import SwiftUI
import MapKit
import FirebaseFirestore
struct MapView: UIViewRepresentable {
var packageLocation: GeoPoint
var destination: GeoPoint
var driverLocation = CLLocationCoordinate2D()
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
MKMapView()
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolygonRenderer(overlay: overlay)
renderer.strokeColor = .blue
renderer.lineWidth = 2.0
return renderer
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
let requestLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: packageLocation.latitude, longitude: packageLocation.longitude)
let destinationLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: destination.latitude, longitude: destination.longitude)
//let span = MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
//let region = MKCoordinateRegion(center: requestLocation, span: span)
//uiView.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = requestLocation
annotation.title = "Package Title"
uiView.addAnnotation(annotation)
let annotation2 = MKPointAnnotation()
annotation2.coordinate = destinationLocation
annotation2.title = "Destiantion"
uiView.addAnnotation(annotation2)
let sourcePlacemark = MKPlacemark(coordinate: requestLocation)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: sourcePlacemark)
directionRequest.destination = MKMapItem(placemark: destinationPlacemark)
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate { (response, error) in
guard let directionResponse = response else {
if let error = error {
print(error.localizedDescription)
}
return
}
print(directionResponse)
let route = directionResponse.routes[0]
uiView.addOverlay(route.polyline, level: .aboveRoads)
let rect = route.polyline.boundingMapRect
uiView.setRegion(MKCoordinateRegion(rect), animated: true)
}
}
}
You've almost got it.
The one issue that you need to resolve is the use of the MKMapView delegate functions.
The easiest way to do that is to subclass MKMapView and make your own map view that has conforms to MKMapViewDelegate.
Firstly, create your own map view, subclassing MKMapView and conforming to MKMapViewDelegate. At the moment you're only really using the rendererFor overlay delegate method so I'll just implement that, but you can add other methods if you require them.
class WrappableMapView: MKMapView, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = .red
renderer.lineWidth = 4.0
return renderer
}
}
Then you need to update your UIViewRepresentable to use the new WrappableMapView that you just created. I have gone for making a functional example, so here I am passing in the request and destination locations. You can handle this how you want but at least this will give you something that works.
struct MyMapView: UIViewRepresentable {
#Binding var requestLocation: CLLocationCoordinate2D
#Binding var destinationLocation: CLLocationCoordinate2D
private let mapView = WrappableMapView()
func makeUIView(context: UIViewRepresentableContext<MyMapView>) -> WrappableMapView {
mapView.delegate = mapView // make sure we set our delegate to be the mapView we just created
return mapView
}
func updateUIView(_ uiView: WrappableMapView, context: UIViewRepresentableContext<MyMapView>) {
let requestAnnotation = MKPointAnnotation()
requestAnnotation.coordinate = requestLocation
requestAnnotation.title = "Package Title"
uiView.addAnnotation(requestAnnotation)
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.coordinate = destinationLocation
destinationAnnotation.title = "Destination"
uiView.addAnnotation(destinationAnnotation)
let requestPlacemark = MKPlacemark(coordinate: requestLocation)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation)
let directionRequest = MKDirections.Request()
directionRequest.source = MKMapItem(placemark: requestPlacemark)
directionRequest.destination = MKMapItem(placemark: destinationPlacemark)
directionRequest.transportType = .automobile
let directions = MKDirections(request: directionRequest)
directions.calculate { response, error in
guard let response = response else { return }
let route = response.routes[0]
uiView.addOverlay(route.polyline, level: .aboveRoads)
let rect = route.polyline.boundingMapRect
uiView.setRegion(MKCoordinateRegion(rect), animated: true)
// if you want insets use this instead of setRegion
// uiView.setVisibleMapRect(rect, edgePadding: .init(top: 50.0, left: 50.0, bottom: 50.0, right: 50.0), animated: true)
}
}
}
Finally we can put it all together with a ContentView that shows it works:
struct ContentView: View {
#State var requestLocation = CLLocationCoordinate2D(latitude: 51.509865, longitude: -0.118092)
#State var destinationLocation = CLLocationCoordinate2D(latitude: 51.501266, longitude: -0.093210)
var body: some View {
MyMapView(requestLocation: $requestLocation, destinationLocation: $destinationLocation)
}
}
This is what it should look like:
One thing to note, using the rendererFor overlay delegate function in the simulator causes an error. This only happens in the simulator and not on device, so don't be surprised if you see an error message like this in the console.
2019-11-08 18:50:30.034066+0000 StackOverflow[80354:9526181] Compiler error: Invalid library file

Mapbox Navigation in iOS with in my mapView controller

I want to integrate Mapbox navigation in iOS, I can easily get the direction/route between two coordinate also to get the navigation path from mapbox we can use below code
let options = NavigationOptions(styles: nil)
let viewController = NavigationViewController(for: self.directionsRoute!)
viewController.delegate=self
self.present(viewController, animated: true, completion: nil)
But the problem is I want to display the navigation in my mapview which is a part of another view controller, I can do that by getting a direction/route and instruction but I can't find any method which will be called every second so that I can update route instruction, as well as route, in case of user change the path.
Let me know if I am missing anything or any changes needed.
-Thanks in advance
here is my approach:
first i did get only directions instructions from the MapBox api taking advantage of it's free API calls quota and draw the instructions on GMSMapView or MapKit taking advantage of their good performance and memory management.
podfile
pod 'MapboxDirections.swift'
import MapboxDirections
this is done through the below code
have the property for MapBox directions
#IBOutlet weak var googleMapView: GMSMapView!
let locationManager = CLLocationManager()
let mapBoxirections = Directions(accessToken: osmToken)
var path: GMSMutablePath?
then do the actual api call
private func drawRouteBetween(source: StopModel, destination: StopModel) {
guard let name = source.name, let lat = source.latitude, let lng = source.longitude else { return }
guard let nameDest = destination.name, let latDest = destination.latitude, let lngDest = destination.longitude else { return }
let waypoints = [
Waypoint(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lng), name: name),
Waypoint(coordinate: CLLocationCoordinate2D(latitude: latDest, longitude: lngDest), name: nameDest),
]
let options = RouteOptions(waypoints: waypoints, profileIdentifier: .automobile)
options.includesSteps = true
options.distanceMeasurementSystem = .metric
mapBoxirections.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
if let route = routes?.first, let leg = route.legs.first {
for step in leg.steps {
if let coordinates = step.coordinates {
for (index, point) in coordinates.enumerated() {
let source = point
if index <= coordinates.count - 2 {
let destination = coordinates[index + 1]
self.drawPolyLine(source: source, destination: destination)
}
}
}
}
}
}
}
note that StopModel is my custom made CLLocation so feel free to replace it with your own as long it has the latitude and longitude
create the method that draws Polyline on your CLLocationManagerDelegate as below
private func drawPolyLine(source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D){
path?.add(source)
path?.add(destination)
let polyLine = GMSPolyline(path: path)
polyLine.strokeWidth = 4 // width of your choice
polyLine.strokeColor = .red // color of your choice
polyLine.map = googleMapView
}
then take a look at the MapBoxDirections.Route model and explore it's properties you will find very useful info inside it
and then take advantage of the callback function from the GMS Delegate that notifies you with the location update instead having a timer and calling it every second this is more efficient way
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
/* do your business here */
}
do not forget to have the delegate of the location manager to self or the class of your choice
Maybe this helps a bit: you can easily add observer for route progress changes:
NotificationCenter.default.addObserver(self,
selector: #selector(progressDidChange(notification:)),
name: .routeControllerProgressDidChange,
object: navigationService.router)
You need a navigation service with your route by creating it like
let navigationService = MapboxNavigationService(route: route)
The function progressDidChange can do something like:
#objc func progressDidChange(notification: NSNotification) {
guard let routeProgress = notification.userInfo?[RouteControllerNotificationUserInfoKey.routeProgressKey] as? RouteProgress,
let location = notification.userInfo?[RouteControllerNotificationUserInfoKey.locationKey] as? CLLocation else {
return
}
// you have all information you probably need in routeProgress, f.E.
let secondsRemaining = routeProgress.currentLegProgress.currentStepProgress.durationRemaining
...
}

GMS Mapview flickers, reloads and crashes when updating coordinates

I have been stuck on this problem for the last three weeks and can't seem to get past it, it's driving me insane. I believe that I have all the correct code, but it is just not ordered properly. I am currently designing an app somewhat like Uber, but a completely different concept.
What I am trying to do is pull down coordinates from Firebase and then drop two "pins" or markers on a GMSMapview. I have a UIView classed as GMSMapview and wired up via an IBOutlet. So when the ViewController loads the Google Maps MapView is in the UIView. What I am trying to accomplish is having a "pin" where the current "driver" is and a second pin where "my" location is. What I want to accomplish is for the map to "zoom & follow" the driver until he arrives at my location, similar to Uber.
I have tried hundreds of different types of code combinations and found articles here on StackOverflow that I followed, but those did not work. I was able to get the pins to show up (green is driver, red is where he is going) and when I went into Firebase and changed one of the coordinates the screen would "jump" or flicker really bad. Doing some more research I read that in order to accomplish this concept (and to show all the "cars" as markers on a GMS MapView as Uber does, in another ViewController) I need to put my coordinates in an array, and then loop through a model containing a struct with the variables. Once I did that, the "flickering" stopped, but then the whole view controller continued to reload from scratch(as if the ViewController was just opened for the first time) every time I updated a coordinate. Sometimes I did latitude and other times longitude, but it made no difference. Obviously since I was updating Firebase manually, i could not do both values at the same time.
The articles I found on StackOverflow seemed very promising and I believe I am on the right track, except after implementing some of the recommended code from Google and here, I am now getting a nil crash (identified in my code below). This crash is occurring after I go into Firebase and manually update a coordinate (either lat or long).
After almost a month of trying to tweak and get this code to work, I am looking for some guidance as to where I have gone wrong with my code. I am using the latest PODS for GoogleMaps and Firebase. Bottom line, I am looking for the way to move the GMS Marker while the coordinates update live in Firebase, as well as have the map zoom in as it gets closer to "my location".
Here are the articles I researched and followed:
GMS Map View Maker Flicker issue
How do I move marker along with moving of Google Map in iOS?
Here is my code:
import UIKit
import CoreLocation
import CoreData
import Firebase
import FirebaseDatabase
import FirebaseAuth
import GoogleMaps
import GooglePlaces
import GooglePlacesPicker
import Alamofire
import SwiftyJSON
class SOPOST: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate, Alertable {
#IBOutlet weak var connectedMapView: GMSMapView!
#IBOutlet weak var driverInfoView: UIView!
let currentUserId = Auth.auth().currentUser?.uid
var markers = [] as NSArray
var locationManager = CLLocationManager()
var placesClient: GMSPlacesClient!
var zoomLevel: Float = 12.0
var likelyPlaces: [GMSPlace] = []
var selectedPlace: GMSPlace?
var custlat: CLLocationDegrees?
var custlong: CLLocationDegrees?
var driverlat: CLLocationDegrees?
var driverlong: CLLocationDegrees?
var destlat: CLLocationDegrees?
var destlong: CLLocationDegrees?
var location: CLLocation?
var destinationMarker = GMSMarker()
var currentCoordAddress: CLLocationCoordinate2D?
var destinationCoordAddress: CLLocationCoordinate2D?
var driverCoordAddress: CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
connectedMapView.delegate = self
DispatchQueue.main.async {
DataService.instance.REF_TRIPS.observe(.value, with: { (snapshot) in
if let findDriverSnapshot = snapshot.children.allObjects as? [DataSnapshot] {
for driver in findDriverSnapshot {
if driver.childSnapshot(forPath: "passengerKey").value as? String == self.currentUserId! {
let acceptanceStatus = driver.childSnapshot(forPath: "tripIsAccepted").value as! Bool
if acceptanceStatus == true {
if let observeAcceptDict = driver.value as? Dictionary<String, AnyObject> {
let pickupCoordinateArray = observeAcceptDict["pickupCoordinate"] as! NSArray
self.custlat = pickupCoordinateArray[0] as? CLLocationDegrees
self.custlong = pickupCoordinateArray[1] as? CLLocationDegrees
let driverCoordinateArray = observeAcceptDict["driverCoordinate"] as! NSArray
self.markers = observeAcceptDict["driverCoordinate"] as! NSArray
self.driverlat = driverCoordinateArray[0] as? CLLocationDegrees
self.driverlong = driverCoordinateArray[1] as? CLLocationDegrees
let prepareLocation = CLLocation(latitude: self.driverlat!, longitude: self.driverlong!)
self.location = prepareLocation
let destCoordinateArray = observeAcceptDict["destinationCoordinate"] as! NSArray
self.destlat = destCoordinateArray[0] as? CLLocationDegrees
self.destlong = destCoordinateArray[1] as? CLLocationDegrees
self.currentCoordAddress = CLLocationCoordinate2DMake(self.custlat!, self.custlong!)
self.destinationCoordAddress = CLLocationCoordinate2DMake(self.destlat!, self.destlong!)
self.driverCoordAddress = CLLocationCoordinate2DMake(self.driverlat!, self.driverlong!)
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
self.destinationMarker.position = CLLocationCoordinate2D(latitude: (self.markers[0] as? CLLocationDegrees)!, longitude: (self.markers[1] as? CLLocationDegrees)!)
self.connectedMapView.camera = GMSCameraPosition.camera(withTarget: self.destinationMarker.position, zoom: 12.0)
self.destinationMarker.icon = GMSMarker.markerImage(with: UIColor.green)
self.destinationMarker.map = self.connectedMapView
self.destinationMarker.tracksViewChanges = false
CATransaction.commit()
let customerdestmarker = GMSMarker()
customerdestmarker.position = CLLocationCoordinate2D(latitude: self.custlat!, longitude: self.custlong!)
customerdestmarker.icon = GMSMarker.markerImage(with: UIColor.red)
customerdestmarker.map = self.connectedMapView
customerdestmarker.tracksViewChanges = false
}
}
}
}
}
})
}
}
func updateLocationoordinates(coordinates:CLLocationCoordinate2D) {
if destinationMarker == nil
{
destinationMarker = GMSMarker()
destinationMarker.position = coordinates
let image = UIImage(named:"destinationmarker")
destinationMarker.icon = image
destinationMarker.map = self.connectedMapView
destinationMarker.appearAnimation = GMSMarkerAnimation.pop
}
else
{
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
destinationMarker.position = coordinates
CATransaction.commit()
}
}
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
self.destinationMarker = GMSMarker(position: self.location!.coordinate) // <----CRASHES HERE: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
destinationMarker.position = position.target
let destinationLocation = CLLocation(latitude: destinationMarker.position.latitude, longitude: destinationMarker.position.longitude)
let destinationCoordinate = destinationLocation.coordinate
updateLocationoordinates(coordinates: destinationCoordinate)
}
}

How to cluster custom icons markers in GoogleMaps for iOS

I'm developing an app on which I want to show a lot of event on the map. The user can click on an event and see a lot of information about it.
I customized the marker icon of each event with a custom image and now I want to cluster each custom markers.
I'm able to cluster the default icon of GoogleMaps API but if I want to cluster my own marker icon I'm not able to do it.
Here's my current code :
var mapView: GMSMapView!
var clusterManager: GMUClusterManager!
let isClustering: Bool = true
let isCustom: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
mapView = GMSMapView(frame: view.frame)
mapView.camera = GMSCameraPosition.camera(withLatitude: 13.756331, longitude: 100.501765, zoom: 12.0)
mapView.mapType = .normal
mapView.delegate = self
view.addSubview(mapView)
if isClustering {
var iconGenerator: GMUDefaultClusterIconGenerator!
if isCustom { // Here's my image if the event are clustered
var images: [UIImage] = [UIImage(named: "m1.png")!, UIImage(named: "m2.png")!, UIImage(named: "m3.png")!, UIImage(named: "m4.png")!, UIImage(named: "m5.png")!]
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [5, 10, 15, 20, 25], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
} else {
}
// Here's my personal marker icon (for one location)
let firstLocation = CLLocationCoordinate2DMake(48.898902, 2.282664)
let marker = GMSMarker(position: firstLocation)
marker.icon = UIImage(named: "pointeurx1") //Apply custom marker
marker.map = mapView
let secondLocation = CLLocationCoordinate2DMake(48.924572, 2.360207)
let secondMarker = GMSMarker(position: secondLocation)
secondMarker.icon = UIImage(named: "pointeurx1")
secondMarker.map = mapView
let threeLocation = CLLocationCoordinate2DMake(48.841619, 2.253113)
let threeMarker = GMSMarker(position: threeLocation)
threeMarker.icon = UIImage(named: "pointeurx1")
threeMarker.map = mapView
let fourLocation = CLLocationCoordinate2DMake(48.858575, 2.294556)
let fourMarker = GMSMarker(position: fourLocation)
fourMarker.icon = UIImage(named: "pointeurx1")
fourMarker.map = mapView
let fiveLocation = CLLocationCoordinate2DMake(48.873819, 2.295200)
let fiveMarker = GMSMarker(position: fiveLocation)
fiveMarker.icon = UIImage(named: "pointeurx1")
fiveMarker.map = mapView
}
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
}
How can I do it?
Look at these screenshots of my app then maybe you could understand better my issue.
First one there are the red default markers icons of the Google Maps, you can see in blue the cluster icon I was added in my project. Then you understand I added some locations on the viewDidLoad(), then the red markers are that. You can also see two others differents markers, the google one is orange and another one is my personal marker icon that I want to use for each marker icon of a location. But you can also see the issue, the issue is the blue cluster icon don't add the markers icons I added on the map (it shows 4 inside the blue cluster icon, it's the 4 icons around it but when the blue cluster icon appears the markers icons around it don't disappear.
Second image, if I make a zoom, the blue cluster icon disappears but you can also see another issue, the locations I was added have another red default markers icons of the Google Maps appears above them (you can see it at less because of my personal orange marker icon
You are actually clustering first then adding markers thats why this is happening.
What you should actually do is
class MarkerModel: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
override func viewDidLoad() {
super.viewDidLoad()
mapView = GMSMapView(frame: view.frame)
mapView.camera = GMSCameraPosition.camera(withLatitude: 13.756331, longitude: 100.501765, zoom: 12.0)
mapView.mapType = .normal
mapView.delegate = self
view.addSubview(mapView)
if isClustering {
var iconGenerator: GMUDefaultClusterIconGenerator!
if isCustom { // Here's my image if the event are clustered
var images: [UIImage] = [UIImage(named: "m1.png")!, UIImage(named: "m2.png")!, UIImage(named: "m3.png")!, UIImage(named: "m4.png")!, UIImage(named: "m5.png")!]
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [5, 10, 15, 20, 25], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
} else {
}
}
func addMarkers(cameraLatitude : Float, cameraLongitude : Float) {
let extent = 0.01
for index in 1...clusterItemCount {
let lat = cameraLatitude + extent * randomScale()
let lng = cameraLongitude + extent * randomScale()
let name = "Item \(index)"
let position = CLLocationCoordinate2DMake(lat, lng)
let item = MarkerModel(position: position, name: name)
item.icon = #imageLiteral(resourceName: "marker")
clusterManager.add(item)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
func randomScale() -> Double {
return Double(arc4random()) / Double(UINT32_MAX) * 2.0 - 1.0
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
let marker = GMSMarker()
if let model = object as? MarkerModel {
// set image view for gmsmarker
}
return marker
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}

Resources