6

I start updating current location when the view did appear, and stop updating location whenever locationManager:didUpdateLocations: is called. But why the locationManager:didUpdateLocations: always be called several times? What have I missed?

#import "ViewController.h"

@interface ViewController (){
    CLLocationManager *locationManager; // location manager for current location
}
@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self startUpdatingCurrentLocation];
}

- (void)startUpdatingCurrentLocation
{
    if (!locationManager)
    {
        locationManager = [[CLLocationManager alloc] init];
        [locationManager setDelegate:self];
        locationManager.distanceFilter = 10.0f; // we don't need to be any more accurate than 10m
    }
    [locationManager startUpdatingLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    [locationManager stopUpdatingLocation];
}
@end
lu yuan
  • 7,207
  • 9
  • 44
  • 78
  • http://stackoverflow.com/questions/22292835/how-to-stop-multiple-times-method-calling-of-didupdatelocations-in-ios – Sanju May 27 '16 at 10:02

3 Answers3

7

Probably it depends about the accuracy you set to the locationManager. You have 3 kinds o localization Cell Radio, WiFi Map, GPS. If you set best as accuracy the location manager will continue to check you position, if the location with better accuracy is out of the range of the distance filter the delegate method will be called again.

Andrea
  • 26,120
  • 10
  • 85
  • 131
  • why [locationManager stopUpdatingLocation] can not stop location updating immediately? – lu yuan Apr 18 '13 at 07:43
  • 1
    May be not, since location queues comes from a hardware buffer, but if it is that the problem there is a simply solution just add a static BOOL before you stop and check it. If your BOOL says that you've already stopped just return from that method. – Andrea Apr 18 '13 at 07:59
  • @Andrea can you please explain more? – Dejell Oct 17 '13 at 17:06
  • @Dejel Which part: - 3 types of localization? - distance filter? - the BOOL? – Andrea Oct 18 '13 at 07:16
  • @Andrea your comment with add a static BOOL – Dejell Oct 19 '13 at 18:42
  • @Dejel : The main problem is that even if after `-stopUpdateLocation` the software continues to enter inside the `-didUpdateLocations`. Is just a guess but I image that those positions are queued up into a buffer. Image the right after you call `-stopUpdateLocation` you have high visibility variable (such as a static global var or an ivar) you toggle this variable (lets call it stopUpdatingLocation) to YES. Inside the `-didUpdateLocations`in the first lines of code, you write `if (stopstopUpdatingLocation==YES) return;`. So the method `-didUpdateLocations`is still called, but it returns. – Andrea Oct 20 '13 at 13:49
  • Well, if you chose a specific accuracy, it's probably because you want this kind of accuracy. So simply adding this BOOL check is far from being a perfect solution. There is no method in CLLocationManagerDelegate that permits to know when the updates are done though ('locationManagerDidPauseLocationUpdates' is called only for AUTOMATICALLY PAUSED updates, not when you voluntarily chose to pause the updates). Maybe using a timer to wait a bit that all the updates are done is a better way. Or just changing the accuracy after all... – Aurelien Porte Feb 28 '14 at 11:14
3

SWIFT version

i made a helper class as HelperLocationManager and added a notification- observer pattern

import UIKit
import CoreLocation



class HelperLocationManager: NSObject {

    var locationManager = CLLocationManager()
    static let sharedInstance = HelperLocationManager()
    var currentLocation :CLLocation?
    var notGotUserLocation = true

    override init() {

        super.init()

        var code = CLLocationManager.authorizationStatus()

        if code == CLAuthorizationStatus.NotDetermined {

            locationManager.requestAlwaysAuthorization()
            locationManager.requestWhenInUseAuthorization()

        }
        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()
        locationManager.delegate = self
        locationManager.distanceFilter = 100;

    }


}


extension HelperLocationManager: CLLocationManagerDelegate{


    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {

            var locValue = locations.last as! CLLocation
            println(locValue)
            self.currentLocation = locValue
            NSNotificationCenter.defaultCenter().postNotificationName("sendCurrentAddressToViewController", object:self.currentLocation)
            notGotUserLocation = false


    }

    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {

        println("Your error is ", error.localizedDescription)

    }


}

Now if your Viewcontroller class needs the location then put an observer there

var helperLocation:HelperLocationManager?

in viewDidLoad as

     override func viewDidLoad() {

         helperLocation = HelperLocationManager()
         NSNotificationCenter.defaultCenter().addObserver(self, selector: "getCurrentAddressToViewController:", name: "sendCurrentAddressToViewController", object: nil)

  }

//and observer as

 func getCurrentAddressToViewController(notification: NSNotification) {

        currentLocation = notification.object as? CLLocation
        NSNotificationCenter.defaultCenter().removeObserver(self, name: "sendCurrentAddressToViewController", object: nil)

    }

//although didUpdateLocation is called multiple times you only get one time location because of removing observer after you get the location.

EDIT: I refractored this helper class so that you dont need to add notificationobserver pattern

class HelperLocationManager: NSObject {

    private lazy var locationManager = CLLocationManager()
    static let sharedInstance = HelperLocationManager()
    var currentLocation :CLLocation?

    override init() {

        super.init()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
    }
}

extension HelperLocationManager: CLLocationManagerDelegate{

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {

        switch status {

        case CLAuthorizationStatus.NotDetermined:

            locationManager.requestWhenInUseAuthorization()


        case CLAuthorizationStatus.Restricted:

            PrinterHelper.messagePrinter("Restricted Access to location")

        case CLAuthorizationStatus.Denied:

            PrinterHelper.messagePrinter("User denied access to location")

        case CLAuthorizationStatus.AuthorizedWhenInUse:

            if #available(iOS 9.0, *) {

                locationManager.requestLocation()

            } else {

                locationManager.startUpdatingLocation()
            }

        default:

            PrinterHelper.messagePrinter("default authorization")

        }
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        let locValue = locations.last
        HelperLocationManager.sharedInstance.currentLocation =  locValue
        locationManager.stopUpdatingLocation()

    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {

        PrinterHelper.errorPrinter(error.localizedDescription)

    }
}

View Controller where you need to get user Permission

var helperLocationManager:HelperLocationManager?

in viewDidLoad as

     override func viewDidLoad() {

         helperLocationManager = HelperLocationManager.sharedInstance


  }

And to get the location you need to call the singleton property currentLocation as

 if  let userCurentLoc = HelperLocationManager.sharedInstance.currentLocation{

           //userCurrentLoc is the user Location

        }
LC 웃
  • 18,888
  • 9
  • 57
  • 72
0

To complement on Anish's answer, if you wanna know when your helper class didn't call the location update (i.e when you have turned off your location services), you can manage this using the locationManager:didChangeAuthorizationStatus: method, and if your location is not allowed, you can call another notification- observer

func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    var shouldIAllow = false

    switch status {
    case CLAuthorizationStatus.Restricted:
        locationStatus = "Restricted Access to location"
    case CLAuthorizationStatus.Denied:
        locationStatus = "User denied access to location"
    case CLAuthorizationStatus.NotDetermined:
        locationStatus = "Status not determined"
    default:
        locationStatus = "Allowed to location Access"
        shouldIAllow = true
    }

    if (shouldIAllow == true) {
        print("Location to Allowed")
        // Start location services
        locationManager!.startUpdatingLocation()
    } else {
        print("Denied access: \(locationStatus)")
        NSNotificationCenter.defaultCenter().postNotificationName("deniedLocation", object:locationStatus)
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
garanda
  • 1,271
  • 11
  • 16