I have load couple of users in map with clustering and now I want to update the users location as per the users new location which I receive from server.
I'm able to do that by clearing the map and load new users data from server but it looks like markers are jumping and it not looks proper.
Is there any way to update the marker locations without clearing the GoogleMap?
Here is the link of my question : Google Map with Cluster and Custom view marker lagging too much while zoomIn and zoomOut
Reference Screen
if some one is moved from his location then how can I update his/her location once I receive updated data from server. (Without clearing the map)
My Code
func setMarkers() {
for i in 0..<SharedData.sharedInstance.allFriends.count {
let marker = GMSMarker()
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
marker.accessibilityHint = String(i)
marker.icon = #imageLiteral(resourceName: "trans")
marker.tracksViewChanges = true
marker.map = mapView
arrMarkers.append(marker)
self.generatePOIItems(String(format: "%d", i), position: marker.position, icon: nil, friend: friend, userIndex: i)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
func updateMarkers() {
for i in 0..<arrMarkers.count {
let marker = arrMarkers[i]
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
marker.accessibilityHint = String(i)
marker.icon = #imageLiteral(resourceName: "trans")
marker.tracksViewChanges = true
marker.map = mapView
}
self.defaultCamera(latitude: SharedData.sharedInstance.userLocation.coordinate.latitude, longitude: SharedData.sharedInstance.userLocation.coordinate.longitude)
}
EDIT
func generatePOIItems(_ accessibilityLabel: String, position: CLLocationCoordinate2D, icon: UIImage?, friend: WallBeeppClass, userIndex: Int) {
let name = "Item \(accessibilityLabel)"
let item = POIItem(position: CLLocationCoordinate2DMake(position.latitude, position.longitude), name: name, friend: friend, userIndex: userIndex)
clusterManager.add(item)
}
func updateMarkers() {
for i in 0..<arrMarkers.count {
let marker = arrMarkers[i]
let friend = SharedData.sharedInstance.allFriends[i]
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
CATransaction.commit()
}
}
EDIT 1
If I change some users location from backend and when I got users new location then I update the marker position using above code but the issue is that new location users are still located at old location. and if I clear & redraw the users data then it works fine.
EDIT 2
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
marker.groundAnchor = CGPoint(x: 0.1, y: 0.45)
if let markerData = (marker.userData as? POIItem) {
let infoWindow = Bundle.main.loadNibNamed("InitialMapInfoView", owner: self, options: nil)?.first as! InitialMapInfoView
infoWindow.imgUser.sd_setImage(with: URL(string: markerData.friend.user_details.user_photo_small), placeholderImage: #imageLiteral(resourceName: "User_profile"), options: .highPriority, completed: nil)
if !markerData.friend.user_details.isUserOnline {
infoWindow.imgCar.image = UIImage.init(named: "small_inactive_" + markerData.friend.user_details.car_personality_name)
}
else {
infoWindow.imgCar.image = UIImage.init(named: "small_" + markerData.friend.user_details.car_personality_name)
}
if markerData.friend.user_details.user_id == 88 {
print("Will Rendrer Marker: \(markerData.friend.user_details.latitude)")
print("Will Rendrer Marker: \(markerData.friend.user_details.longitude)")
}
infoWindow.lblName.text = markerData.friend.user_details.name
infoWindow.btnImgVW.tag = markerData.userIndex
infoWindow.btnImgVW.addTarget(self, action: #selector(btnUserTapped(_:)), for: .touchUpInside)
marker.accessibilityHint = String(markerData.userIndex)
marker.iconView = infoWindow
marker.tracksViewChanges = false
}
Please guide me to do this.
you can subclass GMSMarker, add id of your objects, then get markers by object id you got from server and update position. Here is some code to explain what I mean
class MyMarker: GMSMarker {
var id: String? = nil
}
add id to your marker
func setMarkers() {
let marker = MyMarker()
let friend = SharedData.sharedInstance.allFriends[I]
marker.id = friend.id
than update by id
for friend in SharedData.sharedInstance.allFriends {
guard
let marker = array.first { $0.id == friend.id }
else { contiunue }
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
}
don't add marker to map again, just change it's position
Related
i am not sure how to make the MKPointAnnotations a button i want to be able to click on the pop up to send me to another screen
import UIKit
import MapKit
import Firebase
//building the pin ticker
class mapViewController: UIViewController, MKMapViewDelegate{
//class so an image can be added to point annotation
/* class CustomPointAnnotation: MKPointAnnotation{
var imageName: String!
}*/
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
//set the map location to a specific location when the view loads
let centerLocation = CLLocationCoordinate2DMake(39.863048 , -75.357583)
//logingitude and latitude that the map will cover
let mapSpan = MKCoordinateSpan(latitudeDelta: 0.001, longitudeDelta: 0.001)
//range that the map will show
let mapRange = MKCoordinateRegion(center: centerLocation, span: mapSpan)
//what we will see on the map
self.map.setRegion(mapRange, animated: false)
addAnnotation()
map.delegate = self
let rotate = CGFloat(180)
let regionradius : CLLocationDistance=300.0
let region = MKCoordinateRegion(center: centerLocation, latitudinalMeters: regionradius, longitudinalMeters: regionradius)
//rotation that shows the map is aligned
map.camera.pitch = rotate;
map.setRegion(region, animated: true)
map.delegate = self
map.isUserInteractionEnabled = true
//allows user to still interact with the items on map
let pitch: CGFloat = 300
let heading = 335.0
var camera: MKMapCamera?
camera = MKMapCamera(lookingAtCenter: centerLocation, fromDistance: regionradius, pitch: pitch, heading: heading)
map.camera = camera!
//disables clickables on map
map.isRotateEnabled = false;
map.isZoomEnabled = false;
map.isScrollEnabled = false;
map.showsCompass = false;
}
//funtion to create annotations
private func addAnnotation(){
let parkSpaceOne = MKPointAnnotation()
let parkSpaceTwo = MKPointAnnotation()
let db = Firestore.firestore()
//grabs all the coordinates of the parking spaces in firebase
db.collection("ParkingSpaces").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
if let coords = document.get("coordinate") {
let point = coords as! GeoPoint
let lat = point.latitude
let lon = point.longitude
//string variable for spot field in firebase
let spotpone = document.get("spot") as! String
let spotptwo = document.get("spot") as! String
//if the spot in firbase matches the string then take the coordinates, add them to an annotation and place on the map
if (spotpone == "p1"){
parkSpaceOne.title = "P1"
parkSpaceOne.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
self.map.addAnnotation(parkSpaceOne)
}
else if (spotptwo == "p2"){
parkSpaceTwo.title = "P2"
parkSpaceTwo.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
self.map.addAnnotation(parkSpaceTwo)
}
}
}
}
}
//
}
}
Take a look at the docs
You can detect when the user selects an annotation:
func mapView(MKMapView, didSelect: MKAnnotationView)
Tells the delegate that one of its annotation views was selected.
As well as
func mapView(MKMapView, annotationView: MKAnnotationView, calloutAccessoryControlTapped: UIControl)
Tells the delegate that the user tapped one of the annotation view’s
accessory buttons.
I'm implementing multiple markers on google map using swift in iOS. I've successfully placed all the markers on the map and can select the marker using 'didTap marker'method. But I'm unable to deselect it. When user select on one marker than the rest of the markers should be deselected.
extension MapVC {
// Mark:- Create Marker and set position
fileprivate func setMarkerOnMap() {
if self.markerArray.count != 0 {
for i in 0...self.markerArray.count - 1 {
let data = self.markerArray[i]
guard let lat = data.lat else {
return
}
guard let lon = data.long else {
return
}
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: Double(lat)!, longitude: Double(lon)!, zoom: zoomLevel)
showMarker(position: camera.target, index: i)
}
}
}
// Mark:- Show marker on map
fileprivate func showMarker(position: CLLocationCoordinate2D, inde x: Int) {
let marker = GMSMarker()
marker.position = position
marker.icon = UIImage(named: "marker-unselected-icon")
marker.accessibilityLabel = "\(index)" // get index from array when click on each marker to identify
marker.map = self.mapView
}
}
//MARK - Map
extension MapVC: GMSMapViewDelegate {
// MARK:- DidTap marker
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker)-> Bool {
if let markerLbl = marker.accessibilityLabel{
index = Int(markerLbl)
}
if marker != self.userMarker{
marker.icon = UIImage(named: "marker-selected-icon")
self.restaurantDetailView.isHidden = false
self.distanceView.isHidden = true
self.infoView.isHidden = true
}
var isReturn = true
mapView.selectedMarker = nil
if self.markerArray.count != 0 {
for i in 0...markerArray.count - 1 {
if i == index {
let storeInfo = markerArray[i]
storeInfo.isSelected = !storeInfo.isSelected
self.destinationLat = Double(storeInfo.lat!)!
self.destinationLng = Double(storeInfo.long!)!
marker.icon = UIImage(named: "marker-selected-icon")
markerArray[i] = storeInfo
} else {
marker.icon = UIImage(named: "marker-unselected-icon")
let storeInfo = markerArray[i]
storeInfo.isSelected = false
markerArray[i] = storeInfo
}
}
} else {
mapView.selectedMarker = nil
isReturn = true
return isReturn
}
return isReturn
}
}
I'm changing the selected marker image in did tap method but unfortunately, it is not working. I don't know what I'm doing wrong. Thanks in Advance.
When user want to select one marker and want to deselect all the other markers then following code would be usefull.
To Deselect all Marker on Map:
for (marker in self.markerArray) {
self.mapview.selectedMarker = nil
}
To Select Marker on Map:
self.mapView.selectedMarker = self.userMarker
So I'm trying to transfer the data I've snapshotted from firebase into the custom info window. I currently have four different categories of activities and as such there are four different marker arrays and functions. I have one generic custom info window that I wish to display the markers Title, rating and difficulty level into.
I am having an issue Ive currently tried appending all data into one structured array and then calling the data from that array but all I get is the same set of data in all the info windows. I want each info window to specifically display the data associated with that GMS Marker.
This is is the function that shows the board activities. I have four of these functions for each activity.
func showBoardIcon() {
ref = Database.database().reference()
ref.child("location").observe(.childAdded) { (snapshot:DataSnapshot) in
if let dict = snapshot.value as? [String:AnyObject] {
if dict["Activity"] as! String == "Board" {
let longitude = dict["Longitude"] as! String
let lattitude = dict["Lattitude"] as! String
let title = dict["Title"] as! String
let key = dict["id"] as! String
self.boardIconArray.insert(coordinate(title: title, carLat: lattitude, carLng: longitude, idKey: key), at: 0)
let n = self.boardIconArray.count
let heightWidth = (self.mapView.frame.height / 12)
for var Marker in 1...n {
let boardMarker = GMSMarker()
let boardIconView = UIImage(named: "boardPin")
let image = boardIconView
let location = CLLocationCoordinate2D(latitude: Double(lattitude)!, longitude: Double(longitude)!)
boardMarker.position = location
boardMarker.icon = image
boardMarker.title = title
boardMarker.icon = self.image(image!, scaledToSize: CGSize(width: heightWidth, height: heightWidth))
func displayBoard() {
if self.boardNumber == "1" {
boardMarker.map = self.mapView
self.arrBoardMarker.append(boardMarker)
} else {
boardMarker.map = nil
}
}
displayBoard()
break
}
}
}
}
}
This is the function that displays the custom info window.
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
let Markerview: infoWindow = UIView.fromNib()
let a = arrCarMarkers.count
let b = arrLegMarker.count
let c = arrWaterMarker.count
let d = arrBoardMarker.count
let all = 0 + a + b + d + c
Markerview.titleLbl.text = arrAllMarkers[key].title
Markerview.ratingLbl.text = ("\(arrAllMarkers[all].rating)/5")
Markerview.difficultyLbl.text = arrAllMarkers[all].diff
Markerview.idKey.text = arrAllMarkers[all].key
transferKey = arrAllMarkers[all].key
Markerview.alpha = 0.8
Markerview.layer.cornerRadius = 30
return Markerview
}
Im not sure if what Im doing is even correct. Like I said I just want the data being snapshotted for each marker to be shown to that specific marker.
So I Managed to solve the issue.
I added
boardMarker.title = key
inside the loop where the Marker is being created.
I then wrote this section of code
func mapView(_ mapView: GMSMapView, markerInfoWindow marker: GMSMarker) -> UIView? {
let markerTitle = marker.title as! String
let Markerview: infoWindow = UIView.fromNib()
filteredStruct = arrAllMarkers.filter{$0.key.range(of: markerTitle) != nil}
print(filteredStruct)
Markerview.titleLbl.text = filteredStruct[0].title
Markerview.ratingLbl.text = filteredStruct[0].rating
Markerview.difficultyLbl.text = filteredStruct[0].diff
transferKey = markerTitle
Markerview.alpha = 0.8
Markerview.layer.cornerRadius = 30
print(transferKey)
return Markerview
}
and it works perfectly!
I have an array of lat lon where Im trying to put all the lat long with markers only 1 marker is showing up on the map. Please help
for item in json {
//print(item["price"])
if let vendor = item["vendor"] as? [String:Any],
let lat = vendor["latitude"] as? Double,
let lon = vendor["longitude"] as? Double,
let termsandcondi = item["termsAndConditions"] as? String,
let pre = item["price"] as? Int ,
let name = item["name"] as? String, !name.isEmpty {
//print(termsandcondi)
//print(pre)
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
let data = ["name":name,"latitude":lat,"longitude":lon,"termsAndConditions":termsandcondi,"price":pre] as! [String : AnyObject]
//print("getting here")
self.myArray.append(data)
DispatchQueue.main.async {
print("Coming here !! Dispatch Queue")
let camera = GMSCameraPosition.camera(withLatitude:12.9716, longitude:77.5946, zoom: 10.0)
let subView = GMSMapView.map(withFrame: CGRect(x: 0, y: 0, width: self.mapView.frame.size.width, height: self.mapView.frame.size.height), camera: camera)
self.nameDeal.text = name as String?
self.pric.text = String(describing: pre)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude:lat, longitude:lon)
marker.title = name
marker.map = self.mapView
//marker.map = self.mapView
}
This method will get you post multiple pins on map.
Put this in a mehod and use
func preparingMap(){
//Filtered data is in array of key values that contains lat long
guard let _filteredData = filteredData else { return }
//Converting lat long to double
var latitude = filteredData?.first?.lATITUDE
var longitude = filteredData?.first?.lONGITUDE
for pins in _filteredData{
let position = CLLocationCoordinate2D(latitude: CLLocationDegrees(Float(latitude)), longitude: CLLocationDegrees(Float(longitude)))
let marker = GMSMarker(position: position)
marker.icon = UIImage(named: "map-location-pin")
marker.map = self.mapView
marker.userData = pins.iD
}
}
I have an app where I have the user chose a location on MapView where their brand is based. When they tap a location on the map it creates an annotation with a title as well as subtitle. What I'm trying to do now is convert this annotation to a PFGeoPoint when the user hits "next" button so that it can be saved to Parse and later queried and displayed on a map for every user to see.
Here is the code that allows the user to create an annotation for their location:
//Annotations
func longpress(gestureRecognizer: UIGestureRecognizer){
let touchPoint = gestureRecognizer.location(in: self.map)
let coordinate = map.convert(touchPoint, toCoordinateFrom: self.map)
let annotation = MKPointAnnotation()
if isAnnotated == true {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("REMOVED")
isAnnotated = false
} else {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("ANNOTATION ADDED")
isAnnotated = true
}
}
What I need now is to save the annotation as a PFGeoPoint, however I'm not too sure how to go about it. It seems that PFGeoPoint wants the location in the form of CLLocation, but I don't know how to convert the annotation to that. I can provide the code I have so far for saving it to Parse if need be, but it's really scrambled as I don't know how to get the annotation into a format that can be saved to Parse. Any help is much appreciated. Thanks!
EDIT - Here is my code, I now just need a way to save the geoPoint variable that includes the annotation coordinates as a PFGeoPoint under the "next" func
import UIKit
import Parse
import MapKit
import CoreLocation
class BrandLocation: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var activityIndicator = UIActivityIndicatorView()
var isAnnotated = false
#IBOutlet var map: MKMapView!
var locationManager = CLLocationManager()
//QUESTIONABLE
var currentLoc: PFGeoPoint! = PFGeoPoint()
//QUESTIONABLE
var MapViewLocationManager:CLLocationManager! = CLLocationManager()
//ANNOTATION DECLARED
let annotation = MKPointAnnotation()
//Defines geoPoint as nil
var geoPoint : CLLocationCoordinate2D! = nil
func createAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
//THIS IS WHERE PFGEOPOINT IS SAVED
#IBAction func next(_ sender: AnyObject) {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
let brandLocation = PFObject(className: "location")
//this is the main problem, I'm not sure how to take the coordinates I've defined in the geoPoint variable and now save them as a PFGeoPoint
geoPoint = PFGeoPoint(location: )
geoPoint["brandLocation"] = self.map.annotations
// let geopoint: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
brandLocation["annotationTitle"] = self.map.annotations
brandLocation["annotationSubtitle"] = self.map.annotations
brandLocation.saveInBackground { (succes, error) -> Void in
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
if error != nil {
self.createAlert(title: "Could not update profile", message: "There was a problem updating your profile")
print(":(((")
} else {
self.createAlert(title: "Profile Updated", message: "Profile details successfully updated")
print("MAPPED")
self.performSegue(withIdentifier: "toUserFeed", sender: self)
}
}
// self.performSegue(withIdentifier: "toUserFeed", sender: self)
}
//VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
self.navigationController?.isNavigationBarHidden = true
self.tabBarController?.tabBar.isHidden = true
navigationController?.navigationBar.barTintColor = UIColor.black
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
let uipgr = UITapGestureRecognizer(target: self, action: #selector(BrandLocation.longpress(gestureRecognizer:)))
//uipgr.minimumPressDuration = 1
//uipgr.numberOfTapsRequired = 1
map.addGestureRecognizer(uipgr)
}
override func viewDidAppear(_ animated: Bool) {
}
//Setting up map & location zoom
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region = MKCoordinateRegion(center: location, span: span)
self.map.setRegion(region, animated: true)
locationManager.stopUpdatingLocation()
}
//Annotations
func longpress(gestureRecognizer: UIGestureRecognizer){
let touchPoint = gestureRecognizer.location(in: self.map)
let coordinate = map.convert(touchPoint, toCoordinateFrom: self.map)
//DECLARED ABOVE
//let annotation = MKPointAnnotation()
//WORKING ON GEOPOINT
// currentLoc = PFGeoPoint(location: MapViewLocationManager.location)
//Declares and defines geoPoint
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
self.geoPoint = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
if isAnnotated == true {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("REMOVED")
isAnnotated = false
} else {
annotation.coordinate = coordinate
annotation.title = PFUser.current()?.username
annotation.subtitle = (PFUser.current()?.username)! + " is based here!"
//removes annotation
let allAnnotations = self.map.annotations
self.map.removeAnnotations(allAnnotations)
//adds annotation
self.map.addAnnotation(annotation)
print("ANNOTATION ADDED")
isAnnotated = true
}
}
use annotation coordinate to set a geopoint.
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
let geopoint: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
for you save function
for annotation in self.map.annotations {
//get location of all annotation
let latitude = annotation.coordinate.latitude
let longitude = annotation.coordinate.longitude
let geoPoint = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
//save in Parse object
let brandLocation = PFObject(className: "location")
brandLocation["annotationTitle"] = annotation.annotationTitle
brandLocation["annotationSubtitle"] = annotation.annotationSubtitle
brandLocation["geoPoint"] = geoPoint
brandLocation.saveInBackground { (succes, error) -> Void in
....
}
}