Reputation: 623
I'm pretty new in programming and this is my first app, so sorry if the approach is very shabby.
I created a helper method to get the user location, because I need to call it from different view controllers so I thought this was a cleaner way to do it. But I don't know why is not working now (no errors, it just show the general view of Europe). But when it was inside the view controller it worked perfectly fine.
I got this new approach from the course I'm doing and I've been researching in many sources. I've also checked this question but I didn't find any solution yet.
Here is the method I created in the GMSClient file. It will get the user location, but if the user disables this option, it will show the default position (centred in Berlin):
extension GMSClient: CLLocationManagerDelegate {
//MARK: Initial Location: Berlin
func setDefaultInitialLocation(_ map: GMSMapView) {
let camera = GMSCameraPosition.camera(withLatitude: 52.520736, longitude: 13.409423, zoom: 8)
map.camera = camera
let initialLocation = CLLocationCoordinate2DMake(52.520736, 13.409423)
let marker = GMSMarker(position: initialLocation)
marker.title = "Berlin"
marker.map = map
}
//MARK: Get user location
func getUserLocation(_ map: GMSMapView,_ locationManager: CLLocationManager) {
var userLocation: String?
locationManager.requestWhenInUseAuthorization()
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
map.isMyLocationEnabled = true
map.settings.myLocationButton = true
} else {
setDefaultInitialLocation(map)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
map.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
//Store User Location
userLocation = "\(location.coordinate.latitude), \(location.coordinate.longitude)"
print("userLocation is: \((userLocation) ?? "No user Location")")
}
}
}
}
This file has also this singelton:
// MARK: Shared Instance
class func sharedInstance() -> GMSClient {
struct Singleton {
static var sharedInstance = GMSClient()
}
return Singleton.sharedInstance
}
And then I call it in my view controller like this:
class MapViewController: UIViewController, CLLocationManagerDelegate {
// MARK: Outlets
@IBOutlet weak var mapView: GMSMapView!
// MARK: Properties
let locationManager = CLLocationManager()
var userLocation: String?
let locationManagerDelegate = GMSClient()
// MARK: Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = locationManagerDelegate
GMSClient.sharedInstance().getUserLocation(mapView, locationManager)
}
Anyone has an idea of what can be wrong?
Thanks!
Upvotes: 1
Views: 913
Reputation: 623
Following what Paulw11 said, I found the faster solution using Notifications.
Send notification from the LocationManager delegate method inside the first view Controller:
class MapViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
} else {
initialLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
let userInfo : NSDictionary = ["location" : location]
NotificationCenter.default.post(name: NSNotification.Name("UserLocationNotification"), object: self, userInfo: userInfo as [NSObject : AnyObject])
}
}
}
Set the second view controller as observer. This way I can store the userLocation and use it later for the search request:
class NeighbourhoodPickerViewController: UIViewController, UITextFieldDelegate {
var userLocation: String?
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(locationUpdateNotification), name: Notification.Name("UserLocationNotification"), object: nil)
}
func locationUpdateNotification(notification: NSNotification) {
if let userInfo = notification.userInfo?["location"] as? CLLocation {
self.currentLocation = userInfo
self.userLocation = "\(userInfo.coordinate.latitude), \(userInfo.coordinate.longitude)"
}
}
Upvotes: 2
Reputation: 9484
I guess the problem is here,
self.locationManager.delegate = locationManagerDelegate
You have created a new instance of GMSClient
, and saved it in the stored property and that instance is set as the delegate property of CLLocationManager
.
You need to do this instead,
self.locationManager.delegate = GMSClient.sharedInstance()
You need to do this because you would want singleton instance of GMSClient
to be the delegate for CLLocationManager
and not a new instance. That way your singleton class would recieve the callbacks from
CLLocationManager class.
To understand more about why your code was not working, I would suggest you read more about Objects, Instances, Instance variables, Singletons, Delegate design pattern.
Upvotes: 1