In Android SDK there is minClusterSize property that when we set it to 1 it show's every elements as cluster not marker.
But in iOS SDK doesn't exist minClusterSize and DefaultRenderer by default show's lower than 3 items as items not cluster.
There is any solution ?
Thanks.
import UIKit
class CustomClusterRenderer: GMUDefaultClusterRenderer {
let GMUAnimationDuration: Double = 0.5
var mapView: GMSMapView?
override init(mapView: GMSMapView, clusterIconGenerator iconGenerator: GMUClusterIconGenerator) {
super.init(mapView: mapView, clusterIconGenerator: iconGenerator)
self.mapView = mapView
}
func markerWith(position: CLLocationCoordinate2D, from: CLLocationCoordinate2D, userData: AnyObject, clusterIcon: UIImage, animated: Bool) -> GMSMarker {
let initialPosition = animated ? from : position
let marker = GMSMarker(position: initialPosition)
marker.userData = userData
marker.icon = clusterIcon
marker.groundAnchor = CGPoint(x: 0.5, y: 0.5)
marker.map = mapView
if animated {
CATransaction.begin()
CAAnimation.init().duration = GMUAnimationDuration
marker.layer.latitude = position.latitude
marker.layer.longitude = position.longitude
CATransaction.commit()
}
return marker
}
func getCustomUIImageItem(userData: AnyObject) -> UIImage {
if let item = userData as? Marker {
return item.merkerIcon
}
return UIImage()
}
override func shouldRender(as cluster: GMUCluster, atZoom zoom: Float) -> Bool {
print("Zoom Level is \(zoom) , and result is \(zoom<=14)")
return zoom <= 14;
}
}
class Marker: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var estate: Estate
init(estate: Estate) {
self.estate = estate
self.position = CLLocationCoordinate2D(latitude: estate.latitude,longitude: estate.longitude)
}
}
add one line on class GMUDefaultClusterRenderer.m
static const NSUInteger kGMUMinClusterSize = 10;
Related
I am implementing Google Maps SDK in a SwiftUI project, In this project I bind markers according with a list of order's locations that I request from server.
I am already binding the markers on map, but now I want to centralize the maps with the right zoom on markers, I already did this on the android app version using bounds, and now I want to do the same on swift. But neither mapView.moveCamera() and mapView.animate() are working.
I am requesting the camera change in updateUIView(_ mapView: GMSMapView, context: Context) after that I bind the markers.
The code:
import SwiftUI
import GoogleMaps
import shared
struct GoogleMapsView: UIViewRepresentable {
private let MARKER_SIZE = 40
private let zoom: Float = 15.0
private let bounds: GMSCoordinateBounds = GMSCoordinateBounds()
#Binding var mappedOrders: Dictionary<Int32, [OrderDTO]>
#Binding var filteredOrders: [OrderItem]
#Binding var showAlert: Bool
#Binding var wichAlert: Int
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: 40.4637, longitude: 3.7492, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Context) {
mapView.clear()
for (key) in mappedOrders.keys {
let orderList: [OrderDTO] = mappedOrders[key]!
orderList.filter { order in
return order.deliveryStateString == "rider_assigned" ||
order.deliveryStateString == "delivery_in_progress" ||
order.deliveryStateString == "pending_rider"
}.forEach{ order in
switch order.deliveryState {
case .deliveryInProgress:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
break
case .pendingRider:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
if(order.restaurantLocation != nil) {
createRestaurantMarker(order: order).map = mapView
}
break
case .riderAssigned:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
if(order.restaurantLocation != nil) {
createRestaurantMarker(order: order).map = mapView
}
break
default:
break
}
}
}
let update = GMSCameraUpdate.fit(bounds)
mapView.animate(with: update)
}
private func createDeliveryPointMarker(order: OrderDTO) -> GMSMarker {
let location = CLLocationCoordinate2DMake(
order.restaurantLocation!.latitude,
order.restaurantLocation!.longitude
)
let marker = GMSMarker(
position: location
)
bounds.includingCoordinate(location)
marker.title = order.user
marker.userData = MarkerDataStored(
order: order,
isClientMarker: true
)
let iconName:String
switch order.deliveryState {
case .pendingRider:
iconName = "flag_red"
break
case .riderAssigned:
iconName = "flag_blue"
break
default:
iconName = "flag_green"
break
}
marker.isTappable = true
marker.icon = resizeImage(
image: UIImage(named: iconName)!,
scaledToSize: CGSize(width: MARKER_SIZE, height: MARKER_SIZE)
)
return marker
}
private func createRestaurantMarker(order: OrderDTO) -> GMSMarker {
let marker = GMSMarker(
position: CLLocationCoordinate2DMake(
order.deliveryLocation!.latitude,
order.deliveryLocation!.longitude
)
)
marker.title = order.user
marker.userData = MarkerDataStored(
order: order,
isClientMarker: false
)
let iconName:String
switch order.deliveryState {
case .pendingRider:
iconName = "restaurant_red"
break
default:
iconName = "restaurant_blue"
break
}
marker.isTappable = true
marker.icon = resizeImage(
image: UIImage(named: iconName)!,
scaledToSize: CGSize(width: MARKER_SIZE, height: MARKER_SIZE)
)
return marker
}
func makeCoordinator() -> MapsDelegate {
let delegate = MapsDelegate()
delegate.onClickMarker = { marker in
filterByRestaurant(marker: marker)
wichAlert = OPEN_RESTAURANT_ORDERS
showAlert = true
}
return delegate
}
private func filterByRestaurant(marker: GMSMarker) {
let data: MarkerDataStored = marker.userData as! MarkerDataStored
filteredOrders.removeAll()
if (data.isClientMarker) {
filteredOrders.append(OrderItem(order: data.order))
} else {
self.mappedOrders[data.order.commercialPremiseId]?.forEach() { order in
filteredOrders.append(OrderItem(order: order))
}
}
}
}
struct MarkerDataStored {
let order: OrderDTO
let isClientMarker: Bool
}
The correct way is :
Change this line
bounds.includingCoordinate(location)
with this
bounds = bounds.includingCoordinate(location)
I'm developing an app on which I want to show a lot of events on the map. The user can click on an event and see a lot of informations about it.
In another view, a user can create a new event then the location and title are stored in Firebase database.
Then when others users watch the GoogleMaps on my app, they are able to see all the events who are a marker in the map.
I want to cluster the markers from Firebase when the user zoom out on the map but it can't work maybe because of my loaded way of the data markers on Firebase.
There's 3 issues:
- I'm not able to cluster my custom marker with orange color.
- The markers and the clusters icon don't appear when the map load, I need to zoom in or zoom out first
- I want the data of marker are show in the infoWindow but I got to use the right data for the marker corresponding on the GoogleMap and Firebase.
- When I click on a cluster icon, it shows the alertController too, but I want only see the alertController when user tap a marker not on the cluster icon.
Here's my current code :
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
init(position: CLLocationCoordinate2D, name: String) {
self.position = position
self.name = name
}
}
class NewCarteViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate, GMUClusterManagerDelegate {
var locationManager = CLLocationManager()
var positionActuelle = CLLocation() // Another current position
var currentPosition = CLLocationCoordinate2D()
var latiti: CLLocationDegrees!
var longiti: CLLocationDegrees!
private var clusterManager: GMUClusterManager! // Cluster
private var maMap: GMSMapView!
var marker = GMSMarker()
let geoCoder = CLGeocoder()
var ref = DatabaseReference()
var estTouche: Bool!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
positionActuelle = locationManager.location!
latiti = positionActuelle.coordinate.latitude
longiti = positionActuelle.coordinate.longitude
currentPosition = CLLocationCoordinate2D(latitude: latiti, longitude: longiti)
let camera = GMSCameraPosition.camera(withTarget: currentPosition, zoom: 10)
maMap = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
maMap.mapType = .normal
maMap.settings.compassButton = true
maMap.isMyLocationEnabled = true
maMap.settings.myLocationButton = true
maMap.delegate = self
self.view = maMap
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: maMap, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm, renderer: renderer)
loadMarker()
}
// Download datas of markers from Firebase Database
func loadMarker() {
ref = Database.database().reference()
let usersRef = ref.child("markers")
usersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if (snapshot.value is NSNull) {
print("not found")
} else {
for child in snapshot.children {
let userSnap = child as! DataSnapshot
let uid = userSnap.key // the uid of each user
let userDict = userSnap.value as! [String: AnyObject]
let latitudes = userDict["latitudeEvent"] as! Double
let longitudes = userDict["longitudeEvent"] as! Double
let bellname = userDict["nom"] as! String
let belltitre = userDict["titreEvent"] as! String
let total = snapshot.childrenCount // Number of markers in Firebase
let positionMarker = CLLocationCoordinate2DMake(latitudes, longitudes)
var diff = Double(round(100*self.getDistanceMetresBetweenLocationCoordinates(positionMarker, coord2: self.currentPosition))/100)
var dif = Double(round(100*diff)/100)
var positionEvenement = CLLocation(latitude: latitudes, longitude: longitudes) // Event location
// Function in order to convert GPS Coordinate in an address
CLGeocoder().reverseGeocodeLocation(positionEvenement, completionHandler: {(placemarks, error) -> Void in
if error != nil {
print("Reverse geocoder a rencontré une erreur " + (error?.localizedDescription)!)
return
}
if (placemarks?.count)! > 0 {
print("PlaceMarks \(placemarks?.count)!")
let pm = placemarks?[0] as! CLPlacemark
var adres = "\(pm.name!), \(pm.postalCode!) \(pm.locality!)"
let item = POIItem(position: CLLocationCoordinate2DMake(latitudes, longitudes), name: "")
// self.marker.userData = item // I delete this line
self.clusterManager.add(item)
self.marker = GMSMarker(position: positionMarker)
self.marker.icon = UIImage(named: "marker-45")
self.marker.title = "\(belltitre)"
self.marker.snippet = "Live de \(bellname)\nLieu: \(adres)\nDistance: \(dif) km"
self.marker.map = self.maMap
} else {
print("Problème avec les données reçu par le géocoder")
}
})
}
self.clusterManager.cluster()
self.clusterManager.setDelegate(self, mapDelegate: self)
}
})
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.name)")
} else {
NSLog("Did tap a normal marker")
}
return false
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: maMap.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
maMap.moveCamera(update)
return false
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
let marker = GMSMarker()
if let model = object as? POIItem { // POIItem class is your MarkerModel class
marker.icon = UIImage(named: "marker-45") // Like this ?
// set image view for gmsmarker
}
return marker
}
// Distance between 2 locations
func getDistanceMetresBetweenLocationCoordinates(_ coord1: CLLocationCoordinate2D, coord2: CLLocationCoordinate2D) -> Double {
let location1 = CLLocation(latitude: coord1.latitude, longitude: coord1.longitude)
let location2 = CLLocation(latitude: coord2.latitude, longitude: coord2.longitude)
var distance = ((location1.distance(from: location2)) / 1000)
return distance
}
// Affiche les boutons du live
func alert(_ sender: AnyObject) {
let alertController = UIAlertController(title: "", message: "", preferredStyle: .actionSheet)
alertController.title = nil
alertController.message = nil
alertController.addAction(UIAlertAction(title: "Accéder au live", style: .default, handler: self.accederLive))
alertController.addAction(UIAlertAction(title: "Infos event", style: .default, handler: self.infosEvent))
alertController.addAction(UIAlertAction(title: "Annuler", style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
// Display infoWindow and alertController
func mapView(_ mapView: GMSMapView!, markerInfoWindow marker: GMSMarker!) -> UIView! {
let infoWindow = Bundle.main.loadNibNamed("InfoWindow", owner: self, options: nil)?.first! as! CustomInfoWindow
self.estTouche = true
if self.estTouche == true {
self.alert(self.estTouche as AnyObject)
} else {
print("estTouche est false")
}
print(self.estTouche)
return nil // infoWindow
}
Sorry it a lil bit long code if you don't understand something let me know, I tried to comment
Here's the screenshot of the googlemap now.
The first screenshot is when I open the map, you can see nothing appears on the map, no cluster icon or isolate marker, it strange.
The second screenshot is when I zoom in one time, so after cluster icon appears and one marker appears too.
What's wrong with my code, I want all cluster icon or marker show when user open the map view.
Firstly loadMarker() should be called after the clusterManager is initialized i.e
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm, renderer: renderer)
then
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
should be placed in loadMarker() after for loop ends.
Your viewcontroller should conform to this protocol GMUClusterManagerDelegate then add these 2 methods in viewcontroller.
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
}
Try this and let me know if this works else we will try something else.
I resolve my question so I post here the solution, thank to Prateek for his help.
I use this topic for solve it too : How to implement GMUClusterRenderer in Swift
First I changed one line of code in a GoogleMaps files SDK:
I found it in my project at this path : Pods/Pods/Google-Maps-iOS-Utils/Clustering/GMUDefaultClusterRenderer.m
This file name is GMUDefaultClusterRenderer.m.
This changement it's for cluster custom marker icon in the cluster
- (void)renderCluster:(id<GMUCluster>)cluster animated:(BOOL)animated {
...
GMSMarker *marker = [self markerWithPosition:item.position
from:fromPosition
userData:item
clusterIcon:[UIImage imageNamed:#"YOUR_CUSTOM_ICON"] // Here you change "nil" by the name of your image icon
animated:shouldAnimate];
[_markers addObject:marker];
[_renderedClusterItems addObject:item];
...
}
Second, I added this function in my Swift file of my map :
private func setupClusterManager() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: maMap,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm,
renderer: renderer)
}
Third, I added a variable in the POIItem class in order to get information from Firebase, to show it in infoWindows of markers:
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
var snippet: String! // I add it here
init(position: CLLocationCoordinate2D, name: String, snippet: String) {
self.position = position
self.name = name
self.snippet = snippet // I add it also here
}
}
I get the informations of markers from Firebase thanks to POIItem class in the following function (before I call the function loadMarker() in order to load the data of each markers from Firebase):
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.name!)")
marker.title = "\(poiItem.name!)"
marker.snippet = "\(poiItem.snippet!)"
self.estTouche = true
if self.estTouche == true {
self.alert(self.estTouche as AnyObject) // If true it shows an alertController
} else {
print("estTouche est false")
}
print(self.estTouche)
} else {
NSLog("Did tap a normal marker")
}
return false
}
Here's the whole code of the solution, it works fine for me.
import UIKit
import GoogleMaps
import CoreLocation
import Firebase
import FirebaseAuth
import FirebaseDatabase
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
var snippet: String!
init(position: CLLocationCoordinate2D, name: String, snippet: String) {
self.position = position
self.name = name
self.snippet = snippet
}
}
class NewCarteViewController: UIViewController, GMSMapViewDelegate, CLLocationManagerDelegate, GMUClusterManagerDelegate {
var locationManager = CLLocationManager()
var positionActuelle = CLLocation()
var positionEvent = CLLocationCoordinate2D()
var currentPosition = CLLocationCoordinate2D()
var latiti: CLLocationDegrees!
var longiti: CLLocationDegrees!
private var clusterManager: GMUClusterManager! // Cluster
private var maMap: GMSMapView!
var marker = GMSMarker()
var ref = DatabaseReference() // Firebase reference
var estTouche: Bool!
let geoCoder = CLGeocoder()
// For load the map
override func loadView() {
let camera = GMSCameraPosition.camera(withLatitude: 48.898902,
longitude: 2.282664, zoom: 12)
maMap = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
self.view = maMap
}
override func viewDidLoad() {
super.viewDidLoad()
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm() // crée un gestionnaire de groupes utilisant l'algorithme
let renderer = GMUDefaultClusterRenderer(mapView: maMap, clusterIconGenerator: iconGenerator) // Le renderer est le moteur de rendu des groupes
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm, renderer: renderer)
loadMarker()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
positionActuelle = locationManager.location!
latiti = positionActuelle.coordinate.latitude
longiti = positionActuelle.coordinate.longitude
currentPosition = CLLocationCoordinate2D(latitude: latiti, longitude: longiti)
maMap.mapType = .normal
maMap.settings.compassButton = true // Boussole
maMap.isMyLocationEnabled = true // User current position icon
maMap.settings.myLocationButton = true // Button for center the camera on the user current position
maMap.delegate = self
}
// Download datas of markers from Firebase Database
func loadMarker() {
ref = Database.database().reference()
let usersRef = ref.child("markers")
usersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if (snapshot.value is NSNull) {
print("not found")
} else {
for child in snapshot.children {
let userSnap = child as! DataSnapshot
let uid = userSnap.key // The uid of each user
let userDict = userSnap.value as! [String: AnyObject] // Child data
let latitudes = userDict["latitudeEvent"] as! Double
let longitudes = userDict["longitudeEvent"] as! Double
let bellname = userDict["nom"] as! String
let belltitre = userDict["titreEvent"] as! String
let total = snapshot.childrenCount // Count of markers save in my Firebase database
print("Total de marqueurs : \(total)")
let positionMarker = CLLocationCoordinate2DMake(bellatitude, bellongitude)
var diff = Double(round(100*self.getDistanceMetresBetweenLocationCoordinates(positionMarker, coord2: self.currentPosition))/100)
var dif = Double(round(100*diff)/100)
var positionEvenement = CLLocation(latitude: latitudes, longitude: longitudes)
// Function in order to convert GPS Coordinate in an address
CLGeocoder().reverseGeocodeLocation(positionEvenement, completionHandler: {(placemarks, error) -> Void in
if error != nil {
print("Reverse geocoder meets error " + (error?.localizedDescription)!)
return
}
if (placemarks?.count)! > 0 {
print("PlaceMarks \((placemarks?.count)!)")
let pm = placemarks?[0] as! CLPlacemark
var adres = "\(pm.name!), \(pm.postalCode!) \(pm.locality!)"
let item = POIItem(position: CLLocationCoordinate2DMake(latitudes, longitudes), name: "\(belltitre)", snippet: "Live de \(bellname)\nLieu: \(adres)\nDistance: \(dif) km") // This line is very important in order to import data from Firebase and show in infoWindow for the right datas for each markers
self.clusterManager.add(item)
self.clusterManager.cluster()
self.clusterManager.setDelegate(self, mapDelegate: self)
} else {
print("Problème avec les données reçues par le géocoder")
}
})
}
}
})
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.name!)")
marker.title = "\(poiItem.name!)" // Title of the marker infoWindow (title from Firebase)
marker.snippet = "\(poiItem.snippet!)" // Same for snippet
self.estTouche = true
if self.estTouche == true {
self.alert(self.estTouche as AnyObject) // Show the alertController because infoWindows can't use button
} else {
print("estTouche est false")
}
print(self.estTouche)
} else {
NSLog("Did tap a normal marker")
}
return false
}
// If I tap a cluster icon, it zoom in +1
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position, zoom: maMap.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
maMap.moveCamera(update)
return false
}
private func setupClusterManager() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: maMap,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm,
renderer: renderer)
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
if let model = object as? POIItem {
self.clusterManager.cluster()
self.clusterManager.setDelegate(self, mapDelegate: self)
}
return nil
}
// Distance between 2 locations
func getDistanceMetresBetweenLocationCoordinates(_ coord1: CLLocationCoordinate2D, coord2: CLLocationCoordinate2D) -> Double {
let location1 = CLLocation(latitude: coord1.latitude, longitude: coord1.longitude)
let location2 = CLLocation(latitude: coord2.latitude, longitude: coord2.longitude)
var distance = ((location1.distance(from: location2)) / 1000)
return distance
}
// Show alertController with 2 buttons and a Cancel button
func alert(_ sender: AnyObject) {
let alertController = UIAlertController(title: "", message: "", preferredStyle: .actionSheet) // Ne me donne pas le bon nom
alertController.title = nil
alertController.message = nil // Supprime la ligne message sous le titre afin de pouvoir centrer le titre
alertController.addAction(UIAlertAction(title: "Accéder au live", style: .default, handler: self.accederLive))
alertController.addAction(UIAlertAction(title: "Infos event", style: .default, handler: self.infosEvent)) // Ou Affichage du profil utilisateur
alertController.addAction(UIAlertAction(title: "Annuler", style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
// The two following functions are used in alertController
func accederLive(_ sender: AnyObject) {
...
}
func infosEvent(_ sender: AnyObject) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Event")
present(vc, animated: true, completion: nil)
}
func mapView(_ mapView: GMSMapView!, markerInfoWindow marker: GMSMarker!) -> UIView! {
let infoWindow = Bundle.main.loadNibNamed("InfoWindow", owner: self, options: nil)?.first! as! CustomInfoWindow
return nil
}
}
Hope it can help someone else.
I add finally my way to load the data of the marker in Firebase in another Swift file (if it can help someone to do it too) :
var locationManage = CLLocationManager()
var positionActuel = CLLocation() // Current position of user
var latitudinale: CLLocationDegrees! // Latitude
var longitudinale: CLLocationDegrees! // Longitude
var m = GMSMarker()
class AddEventViewController: UIViewController, UISearchBarDelegate, GMSMapViewDelegate, CLLocationManagerDelegate {
var categorie: String! // Type of event
var titre: String! // Title of event
var name: String! // Username
var userId = Auth.auth().currentUser?.uid // Get the uid of the connected user in Firebase
#IBOutlet weak var titreTextField: UITextField! // TextField in where user can set a title
override func viewDidLoad() {
super.viewDidLoad()
...
locationManage.delegate = self
locationManage.requestWhenInUseAuthorization()
positionActuel = locationManage.location!
latitudinale = positionActuel.coordinate.latitude
longitudinale = positionActuel.coordinate.longitude
name = Auth.auth().currentUser?.displayName // In order to get the username of the connected user in Firebase
}
#IBAction func goAction(_ sender: Any) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Live")
self.present(vc, animated: true, completion: nil)
if titreTextField.text == "" {
titre = "\(name!) Live \(categorie!)"
} else {
titre = titreTextField.text!
}
setMarker(marker: m) // Add a marker on the map
}
// Save data of this event in Firebase (and this marker on Firebase)
func setMarker(marker: GMSMarker) {
var lati = latitudinale
var longi = longitudinale
var nom = self.name
var title = self.titre
var userMarker = ["nom": nom!, // User name
"latitudeEvent": lati!, // Latitude of event
"longitudeEvent": longi!, // Longitude of event
"titreEvent": title!] as [String : AnyObject] // Title of event
KeychainWrapper.standard.set(userId!, forKey: "uid")
let emplacement = Database.database().reference().child("markers").child(userId!) // Reference of my Firebase Database
emplacement.setValue(userMarker)
}
}
I am working on Swift Google Maps. I cannot get the didTapMarker delegate to execute when the user taps on the marker. I put the mapView.delegate = self in the viewDidLoad method, but it did not help.
import UIKit
import GoogleMaps
import Alamofire
import ObjectMapper
import SwiftyJSON
class ViewController: UIViewController, GMSMapViewDelegate, GMUClusterManagerDelegate, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
lazy var mapView = GMSMapView()
private var clusterManager: GMUClusterManager!
//true - marker clustering / false - map markers without clustering
let isClustering : Bool = false
//true - images / false - default icons
let isCustom : Bool = false
var userLocation: CLLocation = CLLocation(latitude: 0, longitude: 0)
override func viewDidLoad() {
super.viewDidLoad()
// User Location
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
locationManager.startUpdatingLocation()
mapView.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations.last!
print("userLocation latitude \(self.userLocation.coordinate.latitude)")
print("userLocation longitude \(self.userLocation.coordinate.longitude)")
let camera = GMSCameraPosition.camera(withLatitude: userLocation.coordinate.latitude,
longitude: userLocation.coordinate.longitude, zoom: 17.0)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.isMyLocationEnabled = true
self.view = mapView
locationManager.stopUpdatingLocation()
//locationManager.delegate = nil
fetch()
}
func fetch () {
let cityLocationService = CityLocationService()
let cityService = cityLocationService.getCityService(latitude: self.userLocation.coordinate.latitude , longitude: self.userLocation.coordinate.longitude)
cityService.fetch(latitude: self.userLocation.coordinate.latitude, longitude: self.userLocation.coordinate.longitude) { (incidents) in
let incidentsJson = incidents
DispatchQueue.main.async(execute: {
self.didGetJson(incidentsJson: incidentsJson)
})
}
}
func didGetJson(incidentsJson: JSON) {
if self.isClustering {
var iconGenerator : GMUDefaultClusterIconGenerator!
if self.isCustom {
var images : [UIImage] = []
for imageID in 1...5 {
images.append(UIImage(named: "m\(imageID).png")!)
}
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [ 10, 50, 100, 200, 500 ], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
self.clusterManager = GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
self.addToMap(resultJson: incidentsJson, isCluster: true)
// Call cluster() after items have been added to perform the clustering and rendering on map.
self.clusterManager.cluster()
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
self.clusterManager.setDelegate(self, mapDelegate: self)
} else {
self.addToMap(resultJson: incidentsJson, isCluster: false)
}
self.view = self.mapView
}
func addToMap(resultJson: JSON, isCluster: Bool) {
for(_, subJson): (String, JSON) in resultJson {
let position = self.checkIfMutlipleCoordinates(latitude: subJson["latitude"].floatValue, longitude: subJson["longitude"].floatValue)
let id = subJson["id"]
let type = subJson["type"].stringValue
let address = subJson["address"].stringValue
// let case_number = subJson["case_number"].stringValue
let date = subJson["date"].stringValue
let markerImage = subJson["markerImage"].stringValue
if isCluster {
let item = POIItem(position: position, name: "#\(id)")
// print("pdid name=\(item.name),latitude=\(item.position.latitude),longitude=\(item.position.longitude).")
clusterManager.add(item)
} else {
let marker = GMSMarker(position: position)
marker.title = "#\(id)"
let image = UIImage(named: markerImage)
marker.icon = image?.resized(to: CGSize(width: 30, height: 30))
marker.opacity = 0.6
marker.snippet = "\r\n date: \(date) id # \(id) \r\n address: \(address) \r\n type: \(type)"
marker.map = mapView
}
}
}
func checkIfMutlipleCoordinates(latitude : Float , longitude : Float) -> CLLocationCoordinate2D{
var lat = latitude
var lng = longitude
let variation = (self.randomFloat(min: 0.0, max: 2.0) - 0.5) / 1500
lat = lat + variation
lng = lng + variation
let finalPos = CLLocationCoordinate2D(latitude: CLLocationDegrees(lat), longitude: CLLocationDegrees(lng))
return finalPos
}
func randomFloat(min: Float, max:Float) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
//Show the marker title while tapping
func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
print("tapped on marker")
if marker.title == "myMarker"{
print("handle specific marker")
}
return true
}
//Optional Feature:
//Add new markers while tapping at coordinates without markers/clusters
func mapView(mapView: GMSMapView, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
let item = POIItem(position: coordinate, name: "NEW")
clusterManager.add(item)
clusterManager.cluster()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Add delegate GMSMapViewDelegate
mapView.delegate = self // in viewDidLoad
If you want to print lat and long of the touched marker, this is the code:
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
print("You tapped : \(marker.position.latitude),\(marker.position.longitude)")
return true // or false as needed.
}
And for long press, if you want to redirect to google maps.
func mapView(_ mapView: GMSMapView, didLongPressInfoWindowOf marker: GMSMarker) {
if (UIApplication.shared.canOpenURL(NSURL(string:"comgooglemaps://")! as URL)) {
UIApplication.shared.open(NSURL(string:"comgooglemaps://?saddr=&daddr=\(marker.position.latitude),\(marker.position.longitude)&directionsmode=driving")! as URL, options: [:], completionHandler: nil)
}
else {
let alert = UIAlertController(title: "Google Maps not found", message: "Please install Google Maps in your device.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
This will work!!!
I am using google map SDK in my application and plotting marker on selected let long and for making groups on marker i am using GMUClusterManager for adding cluster on marker, but with this i am facing an issue to change the image of marker, with if i draw a marker with GMUClusterManager then there no option to change marker image. So any one know any other way to make groups of all markers when user zoom in map, or to change the image of maker.
After creating your GMUDefaultClusterRenderer set its delegate I used the view controller I was working in, and then implement the GMUClusterRendererDelegate
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
After that you implement the func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) from the protocol. This method gives you access to the marker and the data enclosed in the marker.
Use an If let statement to access the data and give the marker the iconView you want if let markerData = marker.userData
Works in Swift 5
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var name: String!
var icon: UIImage
init(position: CLLocationCoordinate2D, name: String, icon: UIImage) {
self.position = position
self.name = name
self.icon = icon
}
}
class PrincipalViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.google_map.clear()
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.google_map, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: self.google_map, algorithm: algorithm, renderer: renderer)
self.clusterManager.setDelegate(self, mapDelegate: self)
//insert your markers type POIItem
//self.clusterManager.add(item)
self.clusterManager.cluster()
}
}
extension PrincipalViewController: GMUClusterRendererDelegate {
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
switch object {
case let item as POIItem:
let marker = GMSMarker()
marker.position = item.position
marker.icon = UIImage(named: "your_custom_marker")
return marker
case let staticCluster as GMUStaticCluster:
let marker = GMSMarker()
marker.position = staticCluster.position
marker.icon = UIImage(named: "your_gruped_custom_marker")
return marker
default:
return nil
}
}
}
extension PrincipalViewController: GMUClusterManagerDelegate {
func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool {
print("didTap clusterItem")
return true
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
print("didTap cluster")
return true
}
}
I am working with Google Map for IOS .Where i have implemented clustering but cluster and cluster item marker click event not work . But individual google map marker click event is working .
Here is my full code
import UIKit
import GoogleMaps
import Alamofire
import SwiftyJSON
let kCameraLatitude = 22.3475
let kCameraLongitude = 91.8123
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var currencies = "currencies";
var phone = "phone";
var dateTime = "dateTime";
var location = "location";
var link = "link";
var exchangeLimits = "exhangeLimits";
var id = "id";
var updated_at = "updated";
var operationName = "opeationName";
var email = "email";
var address = "address";
var createdAt = "createdAt";
var workingDays = "workingDays";
var longitude = "longitude";
var latitude = "latitude";
var exchange = "exChange";
init(position: CLLocationCoordinate2D, operationName: String) {
self.position = position
self.operationName = operationName
}
}
class FirstViewController: UIViewController , GMUClusterManagerDelegate, GMSMapViewDelegate{
private var mapView: GMSMapView!
private var clusterManager: GMUClusterManager!
override func loadView() {
let camera = GMSCameraPosition.camera(withLatitude: kCameraLatitude,
longitude: kCameraLongitude, zoom: 6)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = self
self.view = mapView
}
override func viewDidLoad() {
super.viewDidLoad()
// Set up the cluster manager with default icon generator and renderer.
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
getAllMapsData()
clusterManager.cluster()
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
clusterManager.setDelegate(self, mapDelegate: self)
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("You tapped at \(coordinate.latitude), \(coordinate.longitude)")
var alert = UIAlertController(title: "Choose Media type" , message: "post any" , preferredStyle: .actionSheet);
var photos = UIAlertAction(title : "Photos" , style: .default , handler: nil)
var videos = UIAlertAction(title : "Videos" , style: .default , handler: nil)
var cancel = UIAlertAction(title : "Calcel" , style: .default , handler: nil)
alert.addAction(photos)
alert.addAction(videos)
alert.addAction(cancel)
present(alert, animated: true , completion: nil)
}
// MARK: - GMUClusterManagerDelegate
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
// MARK: - GMUMapViewDelegate
func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.operationName)")
print("clicked cluster")
} else {
NSLog("Did tap a normal marker")
print("clicked marker")
}
return false
}
var allDataList = [[String:AnyObject]]()
public func getAllMapsData(){
Alamofire.request("http://coinatmfinder.com/getDatas").responseJSON { response in
if let JSON = response.result.value {
self.allDataList = JSON as! [[String : AnyObject]]
for eachData in self.allDataList {
//let allDataModel = AllDataModel()
// allDataModel.address = eachData["address"] as! String
var operatorName = eachData["operatorName"]
var lat = eachData["latitude"]
var latDouble = 0.0;
if let lats = lat {
latDouble = (lats as! NSString).doubleValue
}
var longs = eachData["longitude"]
var longsDouble = 0.0;
if let longs = longs {
longsDouble = (longs as! NSString).doubleValue
}
let item = POIItem(position: CLLocationCoordinate2DMake(latDouble, longsDouble), operationName: operatorName as! String)
self.clusterManager.add(item)
}
print("JSON: \(self.allDataList)")
}
}
}
}
Was working on this today and also tried the same function and found it wasn't working because it was the wrong one. Instead of:
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
Try this one: (it looks almost the same as the one above except it has a return type of Bool).
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
}
(Using Swift 3)