I created location manager with
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
but location updates received once per second.
Is it possible to get updates more often?
I tried to get current location in timer, but it's still updated once per second.
Here's what I did:
[self pingLocation];//viewDidLoad
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
[manager stopUpdatingLocation];
manager.delegate = nil;
self.currentLocation = (CLLocation *)[locations lastObject];
}
- (void) pingLocation {
[locationManager startUpdatingLocation];
locationManager.delegate = self;
[self performSelector:#selector(pingLocation) withObject:nil afterDelay:0.5];//change to whatever you want
}
This seemed to give me fairly good results and could make it update more than once per second. However, it drains battery very drastically and is not the best practice. I would ask yourself: why do you want this? Do you really need more than one update per second?
Edit: I've just tested this again and I believe that although it appears to update every half second, it doesn't really. For example:
30.00000, 60.00000
30.00000, 60.00000
30.00032, 60.00056
30.00032, 60.00056
Thus, you really can't update faster than a second, sorry to say.
According to the CLLocation.startUpdatingLocation documentation:
Calling this method several times in succession does not automatically
result in new events being generated. Calling stopUpdatingLocation in
between, however, does cause a new initial event to be sent the next
time you call this method.
You will probably have to also set distanceFilter to none. However, you can imagine these two will help churn through a battery.
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
[locationManager startUpdatingLocation];
}
By this, location manage get update more often. Because every time when it get update it will request for another location frequently.
Related
I have a location app that needs to get accurate location periodically. Currently I am getting constantly getting location in didUpdateLocation but I only ever log the location every 5 seconds. I am interested in a solution that gets accurate location periodically or on signification change. I would like either or both of these scenarios:
(by very accurate, I need 10m of desired accuracy)
Get a very accurate location every 5 seconds
Notify/callback if user moves a threshold ( eg moves 5 - 10 meters)
The app needs to work when backgrounded as well and location must still be logged if user switches to another app.
I was considering turning on/off location every 5 seconds but was not sure if that is the best practice. I also know there is also allowDeferredLocationUpdatesUntilTraveled but I believe that only applied to backgrounded mode. I would appreciate a solution that saves battery when the app is in use and in background mode. Please share your solutions and best practices for my use case.
I did write an app using Location services, app must send location every 10s. And it worked very well.
Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.
Steps are as follows:
Required: Register background mode for update Location.
Create LocationManger and startUpdatingLocation, with accuracy and filteredDistance as whatever you want:
-(void) initLocationManager
{
// Create the manager object
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
_locationManager.delegate = self;
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
_locationManager.desiredAccuracy = 45;
_locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[_locationManager startUpdatingLocation];
}
To keep app run forever using "allowDeferredLocationUpdatesUntilTraveled:timeout" method in background, you must restart updatingLocation with new parameter when app moves to background, like this:
- (void)applicationWillResignActive:(UIApplication *)application {
_isBackgroundMode = YES;
[_locationManager stopUpdatingLocation];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
}
App gets updatedLocations as normal with "locationManager:didUpdateLocations:" callback:
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
self.userLocation = newLocation;
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
But you should handle the data in then "locationManager:didFinishDeferredUpdatesWithError:" callback for your purpose
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
//do something
}
NOTE: I think we should reset parameters of LocationManager each time app switches between background/forgeround mode.
Hopefully this should help
I am using CoreLocation to grab the users location. I need to grab the users location then send that to the server. I have it all working, but! I am implementing locationManager:(CCLLocationManager *)manager...
My url request is hitting before the location is found. Whats the best way to:
Request location
Store in string
Send string to request
I want to ensure the location is found before its sent. Do I check against the instance of the class with a conditional and inside the block hit the server. I dont need to keep updating, just grab once and stopUpdatingLocation
Was going through this post previously: Getting Longitude/Latitude of the User When the viewDidLoad
What I have so far:
- (void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[locationManager startUpdatingLocation];
...more below... server request below here
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSLog(#"found!");
[locationManager stopUpdatingLocation];
}
}
Thoughts on how to do that? I think the conditional may work, but not sure if there was a proper way to handle that using the methods provided from CoreLocation.
Create a new method that will perform your location-dependent code. Then once you have the location (I.e. After the NSLog(#"found!"); line), call this method.
Note that the method may get called multiple times as the location accuracy improves - you might want to handle this.
For example:
- (void)viewDidLoad {
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil)
{
NSLog(#"found!");
[self handleLocation:newLocation];
[locationManager stopUpdatingLocation];
}
}
- (void)handleLocation:(CLLocation*)location
{
// handling code here
}
The location manager is asynchronous. You have to ask for location updates, then wait for it to call you back.
Worse, the first several updates are usually garbage that needs to be thrown away.
The first location update frequently has a timestamp that is hours, days, or even weeks old. You first have to check to make sure the timestamp is in the last few seconds.
Once you've done that, you need to check the horizontal accuracy, and make sure the reading is accurate enough. Often when you first start the location manager the first few readings have an accuracy value of more than a kilometer, which is awful. (Accuracy is really a radius value. You can only be sure that the location you get is somewhere inside a circle with the specified radius.) You need to come up with an app-specific accuracy reading that is "good enough", and throw away accuracy readings until you get one that is good enough. You also need to check for negative accuracy readings, which mean that the GPS is returning invalid values.
Next, you have to allow for the case where the GPS doesn't settle down in a reasonable time. (Sometimes it can take multiple minutes, or simply fail to get a good reading.) In that case you need to handle it as a failure.
So, to handle all that you need to write your locationManager:didUpdateLocation: method to check the time stamp and the horizontal accuracy to make sure the reading is actually good. You also need to make sure you time out and report a failure if you can't get a decent location reading after a reasonable wait. One way to do this is to start a "give up" timer when you first begin location updates, and after you get a good reading, kill it. If the timer fires, stop location updates and report a "can't get a good location reading" error to your user. Understand that it's not uncommon to take 10 or 15 seconds to get a decent accuracy reading.
So I need my delegate updated with a new location when ever the device has moved 50 metres or more. This is part of an end of chapter quiz in a book I'm learning with.
Here is my code:
- (id)initWithCoder:(NSCoder *)aDecoder //we're overriding the superclasses (UIViewController) initialiser
{
self = [super initWithCoder:aDecoder];
if (self){
//create location manager object
locationManager = [[CLLocationManager alloc] init];
//there will be a warning from this line of code
[locationManager setDelegate:self];
//and we want it to be as accurate as possible
//regardless of how much time/power it takes
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
//set the amount of metres travelled before location update is made
[locationManager setDistanceFilter:50];
//tell our manager to start looking for its location immediately
[locationManager startMonitoringSignificantLocationChanges];
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
NSLog(#"%#", [locations lastObject]);
}
I have set the distance filter to 50 (I hope that corresponds to 50 metres). Then when printing to console I've called the last object in the array which I hope would be the most recently updated location.
I then build and run and the initial location is printed to console. I then click debug > location > city run in my iOS simulator menu. I guess this takes the location passt 50 metres as a city run would definitely be more than 50 metres. The location is updated again. I try to update the location again by clicking freeway drive and nothing happens.
In the docs it states: "It does not rely on the value in the distanceFilter property to generate events. Calling this method several times in succession does not automatically result in new events being generated"
This is exactly what I'm experiencing. I can change location once and have the new location printed to console.. however changing the location again results in no changes.
I thought the -locationManager:didUpdateLocations: updates console with the new location every time it's updated. Do I need to do this some other way?
Hope you can help
Kind regards
You forgot to call: startUpdatingLocation like this:
if ([CLLocationManager locationServicesEnabled]) {
[self.locationManager startUpdatingLocation];
}
{
...
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSLog(#"myLocation1: %#",[locations lastObject]);
myLocation = [locations lastObject];
NSLog(#"myLocation2: %#",[locations lastObject]);
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
Currently I'm in the location 40.000,40.000.
I'm closing my app and change location to 10.000,10.000
When entering the app again and running [locationManager startUpdatingLocation]; my log will show:
myLocation1: <+40.00000000,+40.00000000>
myLocation2: <+40.00000000,+40.00000000>
If I'll trigger [locationManager startUpdatingLocation]; again my log will show:
myLocation1: <+10.00000000,+10.00000000>
myLocation2: <+10.00000000,+10.00000000>
How can I call didUpdateLocations once and still get the current location?
Should I use another delegate?
I guess I could place stopUpdatingLocation inside doSomethingWithLocation and run doSomethingWithLocation after some sort of delay in order for the right location to be updated but I'm sure that's not the way it's meant to be.
Thanks
Leave the location manager running for a while (e.g. 30 seconds), setting a timer to tell it to stop. The location manager updates are like pancakes, the first one you get isn't always the best.
The first update you are seeing is likely a "stale" location, which was determined many minutes ago when location services were last powered up. Or it may be a very inaccurate location determined using cell-tower positioning, for example. If you just need to get the device's current location, using Core Location directly requires a good deal of code because you must handle these cases. (The CLLocationManager API appears to be built for apps that need continuous location updates, like turn-by-turn GPS navigation apps.)
Instead of using CLLocationManager directly, I suggest you take a look at using an open source component such as INTULocationManager which will handle all of this work for you and make it trivially simple to request one or more discrete requests for the device's current location.
In this case you should check timestamp of location. User does not move on such distances so quickly.
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *location = [locations lastObject];
if(fabs([location.timestamp timeIntervalSinceNow]) < 5)//seconds
{
myLocation = location;
manager.delegate = nil;
[manager stopUpdatingLocation];
[self doSomethingWithLocation];
}
}
I have three points, but really only the first one is the most important
In order to preserve battery life, I'm trying to have the locationManager turn on just long enough to get the user's location and then shut off. What is the best means to do so?
I don't always need to know the user's location, but having a relatively accurate location when the user hits search is important (within perhaps a couple dozen city blocks, 10,000 meters, maybe even less accurate).
I'm sending a request to a server and then getting the results, and after THAT delay, I need a more accurate version of the user location (within 100 meters is fine).
I don't know how much of this is too nitpicky, but if the last two points are possible/efficient battery-wise, then please let me know how to do it!
You can easily stop tracking location when your CLLocationManager's delegate receives a location update meeting your desired accuracy.
To start updating location, do something like:
CLLocationManager *locationManager = [CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[locationManager startUpdatingLocation];
Then implement the appropriate CLLocationManagerDelegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *foundLocation = [locations lastObject];
if (foundLocation && foundLocation.horizontalAccuracy < kYourDesiredAccuracyInMetres)
{
[manager stopUpdatingLocation];
//Do whatever else with the location you've established
}
}
You should be able to tweak this meet your requirements, by checking the accuracy of the returned locations returned and either stopping the updates or letting them continue (if you need better accuracy).
It's also a good idea to set a timer when you start updating location, and stop updates if you haven't found a location within a set amount of time. You should also implement the locationManager:didFailWithError: delegate method to check whether you can access location services at all.
LocationManager could be stopped after location information of user has been retrieved .
CLLocationManager* locationManager = [ [ CLLocationManager alloc] init];
locationManager.delegate = self; //we must implement the protocol
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.headingFilter = kCLHeadingFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
//To turn on gps (if it isn't on already)
[locationManager startUpdatingLocation];
//To turn gps off (if no other apps are listening)
[locationManager stopUpdatingLocation];