如何在 iOS 的 GoogleMaps 中聚集来自 Firebase 的标记
How to clustered markers from Firebase in GoogleMaps for iOS
我正在开发一个应用程序,我想在其地图上显示很多事件。用户可以单击一个事件并查看有关它的大量信息。
在另一个视图中,用户可以创建一个新事件,然后将位置和标题存储在 Firebase 数据库中。
然后当其他用户在我的应用程序上观看 GoogleMaps 时,他们能够看到地图中作为标记的所有事件。
当用户在地图上缩小时,我想将来自 Firebase 的标记聚集在一起,但它无法工作可能是因为我在 Firebase 上加载数据标记的方式。
有3个问题:
- 我无法将我的自定义标记与橙色聚集在一起。
- 地图加载时标记和集群图标不出现,我需要先放大或缩小
- 我希望标记的数据显示在 infoWindow 中,但我必须为 GoogleMap 和 Firebase 上对应的标记使用正确的数据。
- 当我点击集群图标时,它也会显示 alertController,但我只想在用户点击不在集群图标上的标记时看到 alertController。
这是我当前的代码:
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
}
抱歉,代码有点长,如果您不明白,请告诉我,我试着发表评论
现在是 googlemap 的屏幕截图。
第一个截图是我打开地图的时候,你看地图上什么都没有,没有集群图标,也没有隔离标记,很奇怪。
第二张截图是我放大一次的时候,所以在出现簇状图标后,也出现了一个标记。
我的代码有什么问题,我希望在用户打开地图视图时显示所有集群图标或标记。
首先应该在 clusterManager 初始化后调用 loadMarker()
即
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm, renderer: renderer)
然后
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
应该放在for循环结束后的loadMarker()
中。
您的 viewcontroller 应符合此协议 GMUClusterManagerDelegate
然后在 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
}
试试这个,让我知道这是否有效,我们会尝试其他方法。
我解决了我的问题,所以我 post 给出了解决方案,感谢 Prateek 的帮助。
我也是用这个题目来解决的:
首先,我更改了 GoogleMaps 文件 SDK 中的一行代码:
我在我的项目中的这条路径中找到了它:Pods/Pods/Google-Maps-iOS-Utils/Clustering/GMUDefaultClusterRenderer.m
此文件名为 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"] // Here you change "nil" by the name of your image icon
animated:shouldAnimate];
[_markers addObject:marker];
[_renderedClusterItems addObject:item];
...
}
其次,我在地图的 Swift 文件中添加了此功能:
private func setupClusterManager() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: maMap,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm,
renderer: renderer)
}
第三,我在 POIItem class 中添加了一个变量,以便从 Firebase 获取信息,以在标记的 infoWindows 中显示它:
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
}
}
我通过以下函数中的 POIItem class 从 Firebase 获得了标记的信息(在我调用函数 loadMarker() 以便从 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
}
这是解决方案的完整代码,对我来说效果很好。
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
}
}
希望能帮到别人。
我最后添加了我的方法,将 Firebase 中的标记数据加载到另一个 Swift 文件中(如果它也可以帮助别人做到这一点):
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)
}
}
我正在开发一个应用程序,我想在其地图上显示很多事件。用户可以单击一个事件并查看有关它的大量信息。 在另一个视图中,用户可以创建一个新事件,然后将位置和标题存储在 Firebase 数据库中。 然后当其他用户在我的应用程序上观看 GoogleMaps 时,他们能够看到地图中作为标记的所有事件。 当用户在地图上缩小时,我想将来自 Firebase 的标记聚集在一起,但它无法工作可能是因为我在 Firebase 上加载数据标记的方式。 有3个问题: - 我无法将我的自定义标记与橙色聚集在一起。 - 地图加载时标记和集群图标不出现,我需要先放大或缩小 - 我希望标记的数据显示在 infoWindow 中,但我必须为 GoogleMap 和 Firebase 上对应的标记使用正确的数据。 - 当我点击集群图标时,它也会显示 alertController,但我只想在用户点击不在集群图标上的标记时看到 alertController。
这是我当前的代码:
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
}
抱歉,代码有点长,如果您不明白,请告诉我,我试着发表评论
现在是 googlemap 的屏幕截图。
第一个截图是我打开地图的时候,你看地图上什么都没有,没有集群图标,也没有隔离标记,很奇怪。
第二张截图是我放大一次的时候,所以在出现簇状图标后,也出现了一个标记。 我的代码有什么问题,我希望在用户打开地图视图时显示所有集群图标或标记。
首先应该在 clusterManager 初始化后调用 loadMarker()
即
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm, renderer: renderer)
然后
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
应该放在for循环结束后的loadMarker()
中。
您的 viewcontroller 应符合此协议 GMUClusterManagerDelegate
然后在 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
}
试试这个,让我知道这是否有效,我们会尝试其他方法。
我解决了我的问题,所以我 post 给出了解决方案,感谢 Prateek 的帮助。
我也是用这个题目来解决的:
首先,我更改了 GoogleMaps 文件 SDK 中的一行代码: 我在我的项目中的这条路径中找到了它:Pods/Pods/Google-Maps-iOS-Utils/Clustering/GMUDefaultClusterRenderer.m 此文件名为 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"] // Here you change "nil" by the name of your image icon
animated:shouldAnimate];
[_markers addObject:marker];
[_renderedClusterItems addObject:item];
...
}
其次,我在地图的 Swift 文件中添加了此功能:
private func setupClusterManager() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: maMap,
clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: maMap, algorithm: algorithm,
renderer: renderer)
}
第三,我在 POIItem class 中添加了一个变量,以便从 Firebase 获取信息,以在标记的 infoWindows 中显示它:
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
}
}
我通过以下函数中的 POIItem class 从 Firebase 获得了标记的信息(在我调用函数 loadMarker() 以便从 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
}
这是解决方案的完整代码,对我来说效果很好。
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
}
}
希望能帮到别人。
我最后添加了我的方法,将 Firebase 中的标记数据加载到另一个 Swift 文件中(如果它也可以帮助别人做到这一点):
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)
}
}