didUpdateLocations called every time when viewWillAppear for Significant Location Change - ios

I am using CLLocationManager and MKMapView in same UIViewController.
I want to call an API only when the significant location change.
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.locationManager.requestAlwaysAuthorization()
self.locationManager.delegate = self
self.locationManager.startMonitoringSignificantLocationChanges()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
self.locationManager.distanceFilter = 500
mapView.showsUserLocation = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locations \(locations)")
}
}
In this, the main issue is whenever I make the app background and foreground didUpdateLocations get called. I want it to be called only when significant location change, not every time when viewWillAppear called.
I found its because of MKMapView, didUpdateLocations is being called.

You can manually check the distance with last saved location. Try this.
var lastLocation: CLLocation?
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = lastLocation, let newLocation = locations.last {
if (lastLocation.distance(from: newLocation) < manager.distanceFilter) {
return
}
}
print("locations \(locations)")
lastLocation = locations.last
}

You can save the last locations and check inside didUpdateLocations method, if not same the take appropriate action.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location : CLLocation = locations.last!
if location.coordinate.latitude != lastLat && location.coordinate.longitude != lastLon{
// take action
}
}

Related

iOS - CLLocation Manager didUpdateLocations being called in one class but not another?

Hi I'm making a program that gets the users location and puts an according annotation on the map. I started by writing all of the code in the View Controller and it gets the location perfectly. Below is the working code in the view controller.
class MapViewController: UIViewController, CLLocationManagerDelegate {
var annotation = MKPointAnnotation()
var userLocation: CLLocation?
#IBOutlet weak var mapView: MKMapView!
var locationManager:CLLocationManager!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineCurrentLocation()
}
func determineCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations[0] as CLLocation
print("user latitude = \(userLocation?.coordinate.latitude)")
print("user longitude = \(userLocation?.coordinate.longitude)")
annotation.coordinate = CLLocationCoordinate2D(latitude: (userLocation?.coordinate.latitude)!, longitude: (userLocation?.coordinate.longitude)!)
annotation.title = "You"
mapView.addAnnotation(annotation)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error \(error)")
}
However now when I try and recreate almost the exact same code in another swift file. didUpdateLocations never gets called. locationManager.startUpdatingLocation() does get called.
Below is my new swift file which I call from the View Controller. Is there any simple concept I'm missing here because I really don't see why this doesn't work.
import Foundation
import CoreLocation
class SendLocation: NSObject, CLLocationManagerDelegate {
var userLocation: CLLocation?
var locationManager:CLLocationManager!
func sendLocationPost() {
determineCurrentLocation()
}
func determineCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
print("WHY")
if CLLocationManager.locationServicesEnabled(){
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations[0] as CLLocation
print("user latitude = \(userLocation?.coordinate.latitude)")
print("user longitude = \(userLocation?.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error \(error)")
}
}
I call it using :
let location = SendLocation()
location.sendLocationPost()`
in my View Controller
This happens because you are not keeping a reference to your SendLocation object.
Make SendLocation a property of your UIViewController.
For example, calling it from a static scope will not keep a reference.
WONT WORK:
static func sendLocation() {
let location = SendLocation()
location.sendLocationPost()
}
WILL WORK
let location = SendLocation()
func sendLocation() {
location.sendLocationPost()
}

fatal error: unexpectedly found nil while unwrapping an Optional value when getting Location?

I am developing an app using swift I want to fetch current location of the user and want to show in google map so I wrote my code in my class I have included all the functions and methods (i.e) added and imported frameworks core location and also updated plist but I can't able to fetch current location rotationally some time I get the location but some time it crashed.
Here is the code what I am tried.
import CoreLocation
class HomeViewController: UIViewController, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
let location: CLLocation? = locationManager.location
let coordinate: CLLocationCoordinate2D? = location?.coordinate ----> App crossed this line then it will crashed
print(coordinate!)
print(coordinate!.latitude)
print(coordinate!.longitude)
strForCurLatitude = "\(coordinate!.latitude)"
strForCurLongitude = "\(coordinate!.longitude)"}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
print("User allowed us to access location")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while get location \(error)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.startUpdatingLocation()
let locationNew = locations.last
let coordinateNew: CLLocationCoordinate2D? = locationNew?.coordinate
strForCurLatitude = "\(coordinateNew!.latitude)"
strForCurLongitude = "\(coordinateNew!.longitude)"
strForLat=strForCurLatitude;
strForLong=strForCurLongitude;
print("User's Latitude is: \(Double(strForLat!)!)")
print("User's Longitude is: \(Double(strForLong!)!)")
self.locationManager.startUpdatingLocation()
}
Remove this from ViewDidLoad because you can not get location in viewDidLoad.
let location: CLLocation? = locationManager.location
let coordinate: CLLocationCoordinate2D? = location?.coordinate
print(coordinate!)
print(coordinate!.latitude)
print(coordinate!.longitude)
strForCurLatitude = "\(coordinate!.latitude)"
strForCurLongitude = "\(coordinate!.longitude)"
In CLLocationManager's following method best for getting location
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
make a reference to CLLocationManager as variable, like this:
class HomeViewController {
var locationManager = CLLocationManager()
viewDidLoad() {
super.viewDidLoad()
}
// rest of code
}
in this case Location Manager should works properly.

How to check if locationManager has updated current location?

I would like to show user's current location on startup, and then keep tracking their location but stop centring on the current location. My idea is to centre on the current location in viewDidLoad(), but I don't know how to wait for locationManager to update the current location before centring. Here are the relevant parts of my code:
var currentLocation : CLLocation?
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
// wait for currentLocation to be updated
animateMap(currentLocation!)
}
mapView.showsUserLocation = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last!
}
func animateMap(_ location: CLLocation) {
let region = MKCoordinateRegionMakeWithDistance(location.coordinate, 1000, 1000)
mapView.setRegion(region, animated: true)
}
You need to simply call that animateMap(currentLocation!) function in delegate method didUpdateLocations where you are initializing the currentLocation.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last!
animateMap(currentLocation!)
//Either Call `stop​Updating​Location()`
locationManager.stop​Updating​Location()
//Or you can create one boolean property with default value false and compare it inside this method
if !animatedFlag {
animateMap(currentLocation!)
animatedFlag = true
}
}
var animatedFlag = false
// MARK:- Location lattitude longtitude method Delegate Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if locationManager.location != nil
{
let locValue = (locationManager.location?.coordinate)!
lat = locValue.latitude
long = locValue.longitude
}
}
Call the animateMap function in didUpdateLocations delegate method, instead of in viewDidLoad. And, allow the map to center only the first time. You can you some bool variable for that.
var isLocationUpdated = false
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last!
if (!isLocationUpdated)
{
animateMap(currentLocation!)
isLocationUpdated = true
}
}

CLLocationManager delegate methods are not getting called(google maps is integrated)

The delegate Methods of CLLocationManager
didChangeAuthorizationStatus
and
didUpdateToLocation
are not getting called.
Location Always Usage Description key is already added in info.plist and I am getting notification also when i launch app for the first time.
I am able to see the google map, but i am not able to see current location, When i change location, its not getting updated. Basicaaly delegate methods are not getting called.
//code
import UIKit
import GoogleMaps
class ViewController: UIViewController,GMSMapViewDelegate {
#IBOutlet weak var mapViewTest: GMSMapView!
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad()
{
super.viewDidLoad()``
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController : CLLocationManagerDelegate
{
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus)
{
if status == .authorizedAlways
{
if(CLLocationManager .locationServicesEnabled())
{
locationManager.startUpdatingLocation()
mapViewTest.isMyLocationEnabled = true
mapViewTest.settings.myLocationButton = true
}
}
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation)
{
mapViewTest.camera = GMSCameraPosition(target: (newLocation.coordinate), zoom: 15, bearing: 0, viewingAngle: 0)
currentLocation = newLocation
currentLatitude = newLocation.coordinate.latitude
currentLongitude = newLocation.coordinate.longitude
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Errors: " + error.localizedDescription)
}
}
From your code you are working with Swift 3, and in Swift 3 CLLocationManagerDelegate method's signature is changed like this.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
//func locationManager(_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation,
from oldLocation: CLLocation) is deprecated with below one
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
Check Apple Documentation on CLLocationManagerDelegate for more details.
After checking your code i found some changes you needs to do as follow,
note : I have only added the code having issue here that of location manager
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?) {
print("Errors: " + (error?.localizedDescription)!)
}
}
Also add below lines in .plist file if not added,
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) location use</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) always uses location </string>
Add these two properties in your info.plist
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
you need to put the mapview delegate to self.
try this:
override func viewDidLoad() {
super.viewDidLoad()
// User Location Settings
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
// Google Maps Delegate
mapView.delegate = self
}
// View will appear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Google maps settings
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
// Get location if autorized
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) {
let (latitude, longitude) = self.getLocation()
mapView.camera = GMSCameraPosition.camera(
withLatitude: latitude,
longitude: longitude,
zoom: 14)
}
}
//Get the user location
func getLocation() -> (latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let latitude = (locationManager.location?.coordinate.latitude)!
let longitude = (locationManager.location?.coordinate.longitude)!
return (latitude, longitude)
}

Getting My Location with google maps swift

I've got this code:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
and this code:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation = locations.last
print(userLocation)
let camera = GMSCameraPosition.cameraWithLatitude(userLocation!.coordinate.latitude,
longitude: userLocation!.coordinate.longitude, zoom: 12.5)
self.mapView.camera = camera
self.mapView.myLocationEnabled = true
// self.mapView.settings.myLocationButton = true
locationManager.stopUpdatingLocation()
}
This was working earlier but now it seems like locationManager isn't even being called as the print statement isn't returning any information at all. There are no errors in the code and I can't seem to figure out why the function isn't being called. Can anyone spot anything obvious that I'm missing?
Thank you
I just implemented this and I'm going to say you what I've done
First, you create a locationManager variable
let locationManager: CLLocationManager = CLLocationManager()
You have to request permission in the viewDidLoad()
viewMap.delegate = self
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
Then, I created an extension and I implemented the delegate
extension MyView: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
//here you configure the locationManager
locationManager.startUpdatingLocation()
viewMap.isMyLocationEnabled = true
viewMap.settings.myLocationButton = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
}
I hope this help you, here works properly.

Resources