lu yuan
lu yuan

Reputation: 7227

locationManager:didUpdateLocations: always be called several times

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

Upvotes: 6

Views: 6480

Answers (3)

LC 웃
LC 웃

Reputation: 18998

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

        }

Upvotes: 3

garanda
garanda

Reputation: 1291

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

Upvotes: 0

Andrea
Andrea

Reputation: 26383

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.

Upvotes: 7

Related Questions