如何在GoogleMaps for iOS中使用Firebase中的标记

我正在开发一个应用程序,我想在地图上显示很多事件。 用户可以点击一个事件,并看到很多关于它的信息。 在另一个视图中,用户可以创build一个新事件,然后将位置和标题存储在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的截图。 打开地图照片 放大后

第一张截图是当我打开地图的时候,你可以在地图上看到没有任何东西出现,没有集群图标或隔离标记,这很奇怪。

第二个屏幕截图是当我放大一次,所以群集图标出现后,也出现一个标记。 我的代码有什么问题,当用户打开地图视图时,我想要显示所有集群图标或标记。

首先loadMarker()应该在clusterManager初始化之后调用

 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 } 

试试这个,让我知道如果这个工作,否则我们会尝试别的。

我解决了我的问题,所以我在这里发布解决scheme,感谢Prateek的帮助。

我也使用这个主题来解决它: 如何在Swift中实现GMUClusterRenderer

首先,我在GoogleMaps文件SDK中更改了一行代码:我在这个path的项目中find了它: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) } 

第三,为了从Firebase获取信息,我在POIItem类中添加了一个variables,以在标记的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 } } 

我从以下函数中获取了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 } 

这是解决scheme的整个代码,对我来说工作得很好。

 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) } }