Reputation: 3495
In my app I need to know user location and then load an array from a DB. So I created an instance of CLLocationManager
@property (nonatomic,strong) CLLocationManager *gps;
In viewDidLoad I set some properties of gps:
- (void)viewDidLoad
{
[super viewDidLoad];
self.gps = [[CLLocationManager alloc] init];
self.gps.delegate = self;
self.gps.desiredAccuracy = kCLLocationAccuracyBest;
}
and in viewDidAppear I start to update user location if my table view is empty:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ([self.tableView numberOfRowsInSection:0] <= 0) {
[self.gps startUpdatingLocation];
}
}
Now when gps find user location, it calls the delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(@"DID UPDATE LOCATIONS");
[self.gps stopUpdatingLocation];
if (!didUpdateLocation) {
didUpdateLocation = YES;
currentLocation = [locations lastObject];
[self performSelectorOnMainThread:@selector(loadDistance) withObject:nil waitUntilDone:YES];
[self caricaLocaliInOrdineAlfabetico:NO];
}
}
The problem is in the delegate method cause SOMETIMES (not always) it is called more than one time even if I ask gps to stopUpdatingLocation. As you can see I also tried to declare a BOOL called didUpdateLocation to check if user location was already taken, but it seems to be useless. How is possible?
Here is the debug log:
2014-05-05 18:22:02.324 AppName[9529:60b] <FMDatabase: 0x16d5e740> executeQuery: SELECT * FROM locali
2014-05-05 18:22:02.483 AppName[9529:60b] DID UPDATE LOCATIONS
2014-05-05 18:22:06.002 AppName[9529:60b] CARICA LOCALI
2014-05-05 18:22:06.005 AppName[9529:60b] LOCALE INDEX: 170
2014-05-05 18:22:06.009 AppName[9529:60b] <FMDatabase: 0x16e6b040> executeQuery: SELECT * FROM locali ORDER BY distanza ASC
2014-05-05 18:22:06.068 AppName[9529:60b] DID UPDATE LOCATIONS
2014-05-05 18:22:09.784 AppName[9529:60b] CARICA LOCALI
2014-05-05 18:22:09.791 AppName[9529:60b] LOCALE INDEX: 170
2014-05-05 18:22:09.802 AppName[9529:60b] <FMDatabase: 0x16e72130> executeQuery: SELECT * FROM locali ORDER BY distanza ASC
Upvotes: 2
Views: 276
Reputation: 3495
I think the best way to avoid the problem is by using a timer. Now, even when the delegate method is called more than one time, there are no problems.
In .h file
NSTimer *loadDistanceTimer;
In .m file
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
currentLocation = [locations lastObject];
float averageAccuracy = (currentLocation.horizontalAccuracy + currentLocation.verticalAccuracy) / 2;
NSLog(@"DID UPDATE LOCATIONS WITH %.0f AV_ACC",averageAccuracy);
if (averageAccuracy < 20) {
[self.gps stopUpdatingLocation];
if (loadDistanceTimer == nil) {
loadDistanceTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(loadDistance)
userInfo:nil
repeats:NO];
}
}
}
- (void)loadDistance
{
// while cycle here
while (something) {
//do something
}
loadDistanceTimer = nil;
[self caricaLocaliInOrdineAlfabetico:NO];
}
As DuncanC said, the first location taken from gps may be inaccurate, so I calculate an average accuracy from horizontalAccuracy and verticalAccuracy to improve the accuracy of current location.
Upvotes: 0
Reputation: 131408
Your premise is flawed.
The location manage gives very bad results when you first ask for location updates, and takes a while to settle down and start giving good results.
You can't take one and only one location reading and assume it's an accurate location. Most often it will be very inaccurate.
You need to check the time stamp on your location updates, and reject updates that are more than a few seconds old. This is because the location manager caches the location it read when the GPS was last running, and will sometimes give you a first location reading that is hours or days out-of-date, and can be wildly wrong.
Next, you'll start getting readings with very poor horizontal accuracy. The first couple of readings can be off by a kilometer or more. You need to be taking updates until you get one that is accurate enough for your purposes. (The accuracy reading is actually a radius value that indicates how much possible error there could be in the reading.)
You need to code your location updates to throw away inaccurate readings, and also eventually time out if you don't get a satisfactory reading in a reasonable amount of time (30 seconds would be a good starting point.)
Only when you get a reading that has a recent timestamp and has a low enough accuracy reading should you process the reading. At that point, set your "didUpdateLocation" flag and stop location updates.
Upvotes: 2
Reputation: 37581
Its not a bug.
didUpdateLocations: can continue to be called for a short while after stopUpdatingLocation has been called so you should code for this.
Upvotes: 1