CLLocation Manager startUpdatingLocation not working second time called - ios

Hi I am implementing Location services in my app. First I have to know my Coordinates to get the distance between some places that I have in a list and the device. Then if I go into a place I can make a check in, so, I need to get coordinates again, and the problem is here. Second time I try to get coordinates, the method -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations is not called.. and I can not get new Coordinates.
My manager is located in a NSObject sublcass with this code:
(id)init {
if ( self = [super init] ) {
if ([CLLocationManager locationServicesEnabled])
{
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
}
}
return self;
}
-(void) checkLongLatitudeAgain {
[locationManager startUpdatingLocation];
}
#pragma mark Delegates de CLLocationManager
//
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(#"LON%f", manager.location.coordinate.longitude);
NSLog(#"LAT:%f", manager.location.coordinate.latitude);
NSTimeInterval howRecentNewLocation = [newLocationeventDate timeIntervalSinceNow];
if (manager.location.horizontalAccuracy <= 100.0 && howRecentNewLocation < -0.0 && howRecentNewLocation > -20.0){
//Usar coordenada
[self.delegate getLocationForCheckIn:manager.location];
[self stopUpdatingLocation:#"Fins"];
}
}
// ---------------------------------------------------------------------------------------------------------------------
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
//
if ([error code] != kCLErrorLocationUnknown) {
[self stopUpdatingLocation:NSLocalizedString(#"Error", #"Error")];
}
//
}
// ---------------------------------------------------------------------------------------------------------------------
- (void)stopUpdatingLocation:(NSString *)state {
//Detenemos la lectura del GPS
[locationManager stopUpdatingLocation];
locationManager.delegate = nil;
NSLog(#"Stop gps");
//
}
I call the class when the list of places is open, and also when inside a place the user press checkIn button. Both times I do it with this code:
WPLocationManager *location = [[WPLocationManager alloc]init];
[location checkLongLatitudeAgain];

You are creating a new manager every time:
WPLocationManager *location = [[WPLocationManager alloc]init];
[location checkLongLatitudeAgain];
That new manager is not assigned to any delegate.
You need to use the previous manager you have created and assigned to your delegate, something like:
[locationManager checkLongLatitudeAgain];

You can check the documentation at http://developer.apple.com - https://developer.apple.com/library/ios/documentation/userexperience/conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html
In particular you can check the Starting the Standard Location Service and Starting the Significant-Change Location Service sections. You have to use the startMonitoringSignificantLocationChanges or startUpdatingLocation method of CLLocationManager, cache your location somewhere and update it only when a new location is received, otherwise like it is stated in the documentation: "If a location update has already been delivered, you can also get the most recent location data directly from the CLLocationManager object without waiting for a new event to be delivered".

i dont know why you are initiating your location manager again again, also even if you some how manage to solve current problem but it's not proper way of dealing with location manage based applications.I had been in trouble previously when i was working on location based app. the best approach for location based app is singleton.
apple forum discussion
you can find
this
and this very helpful.
just an advice, :)
Thanks.

In iOS8 for me I had to call [locationManager stopUpdatingLocation]; before calling [locationManager startUpdatingLocation] to start getting updates second time and it works for me.

Related

iOS: When I try to get CLLocation by click, there is most of the time a "big" lag

I'm first using the CoreLocation framework. I have a table and by button click a new location should be added and the distance to all entries in the table should be shown and updated all the time. That is why I have a BOOL saveNewLocation which is set to Yes when the button is clicked. Because the updates need to happen still all the time in the background, but when the button is clicked only a new entry is added.
At the moment I have this in my viewDidLoad:
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
And this is my delegate method:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
self.currentLocation = newLocation;
if(self.saveNewLocation){
[PointOfInterest addPointOfInterest:newLocation withAddress:#"" andNotes:#"" inManagedObjectContext:self.cdh.context];
self.saveNewLocation = NO;
}
[self updateAllDistances];
}
And this my button:
- (IBAction)addLocationClicked:(id)sender {
self.saveNewLocation = YES;
}
But the problem at the moment is that when you click this button, there is sometimes a big lag and nothing happens. Sometimes immediately a new location is added. How can I avoid this lag and instantly add a new location by click?
The time interval between update calls to the location manager delegate is variable, so the behavior you're experiencing is expected.
CLLocationManager has a property called location which returns the last known location of the user (or nil if you've never used the Location Manager in the app).
Instead of waiting for the LocationManager to update, grab the last known location of the user instead:
- (IBAction)addLocationClicked:(id)sender {
CLLocation *location = self.locationManager.location;
if (location && [NSDate timeIntervalSinceReferenceDate] - location.timeStamp.timeIntervalSinceReferenceDate < 60 * 10){
//Do something with the location if the location manager returns a location within the last 10 minutes
} else {
self.saveNewLocation = YES;
}
}
If the app has never asked for it's location, you may get nil, in which case you'll have to wait for the locationManager to update. But otherwise, you can just grab the last known location. You can also check whether the location was update recently by checking the timeStamp on the location object.
You also may want to set a state flag indicating that the app should wait for a location update when the location manager is first used. When you first start up the LocationManager, you can't really know how up-to-date that location is. But once the manager begins updating the delegate, you can be reasonably certain the location manager holds a fairly up-to-date location.

AFNetworking and CLLocation Manager iOS

My question is as follows:
When is the location updated when using Location Services? When I called startUpdatingLocation I expected to already have a location returned so I can retrieve latitude and longitude for my iOS project. These are required parameters for a web service as well but it seems they are returned as nil.
The interface conforms to CLLocationManagerDelegate protocol and I have implemented the methods for it. Anyway here is my code:
- (void)viewDidLoad
{
super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
if([self.parentViewController isKindOfClass:[BTMainViewController class]])
{
BTMainViewController *parent = (BTMainViewController *)self.parentViewController;
self.sessionKey = parent.session;
NSLog(#"URL is %# ", self.sessionKey);
}
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// also set the URL
self.serviceURL = [apiURL stringByAppendingString:#"/get_employee_closestlocations"];
// set tableview delegate and data source
self.tableView.delegate = self;
self.tableView.dataSource = self;
// adjust for EdgeInset with navigation bar.
self.tableView.contentInset = UIEdgeInsetsMake(64.0f, 0.0f, 0.0f, 0.0f);
// fetch the locations here
[locationManager startUpdatingLocation];
[self fetchLocations];
}
didUpdateToLocation implementation
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
CLLocation *currentLocation = [locationManager location];
[locationManager stopUpdatingLocation];
if(currentLocation != nil)
{
[self setLongitude:[NSNumber numberWithDouble: currentLocation.coordinate.longitude]];
[self setLatitude:[NSNumber numberWithDouble: currentLocation.coordinate.latitude]];
}
}
Any suggestions would be welcome and thanks in advance!
The delegate method you are using is deprecated. You should use locationManager:didUpdateLocations: and then access the location update from the end of the array -
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *currentLocation = (CLLocation *)[locations lastObject];
...
}
It can take some time to get a location fix, particularly as you have specified kCLLocationAccuracyBest - iOS may need to start up the GPS receiver if it hasn't been used recently and then the GPS needs to obtain a fix - if the device is inside or has bad GPS reception this can further delay the acquisition of a location. You can get an idea of the time to obtain a fix by restarting your device, starting the maps application and tapping the location "arrow" and waiting until the blue location circle collapses down to the blue & white marker.
I would suggest that you invoke your [self fetchLocations]; from the didUpdateLocations method
Also, the Core Location documentation states -
When requesting high-accuracy location data, the initial event
delivered by the location service may not have the accuracy you
requested. The location service delivers the initial event as quickly
as possible. It then continues to determine the location with the
accuracy you requested and delivers additional events, as necessary,
when that data is available.
So, there is a risk that when you do access the location, it may not be particularly accurate. You can look at the horizontalAccuracy property of the CLLocation and decide whether you want to accept this location or wait for a more accurate location (bearing in mind that it may not arrive if the device is inside or has poor reception)
You need to do in viewDidLoad like this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
mapView.delegate = self;
mapView.showsUserLocation = YES; // Enable it when we want to track user's current location.
}
after doing this the below delegate method will automatically called.
- (void)mapView:(MKMapView *)mapView
didUpdateUserLocation:
(MKUserLocation *)userLocation
{
self.mapView.centerCoordinate = userLocation.location.coordinate;
}

Best technique for counting laps?

I'm making an app that needs to count laps for participants walking around a track. I thought that I could create a small geo-fence around the start point and have the OS let me know when the user entered it, however this doesn't seem to work as well as I was hoping. It seems that it isn't triggered reliably when the walker enters the fence or triggers in other areas around the track. (I'm testing without letting the device go to sleep at this time).
It also seems that the locationManager:didExitRegion method isn't called properly in my testing.
When the user taps the start button, the following method is called:
-(void)startLocationManager {
if ([CLLocationManager locationServicesEnabled] && usingLocationManager) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLLocationAccuracyBest;
self.locationManager.activityType = CLActivityTypeFitness;
[self.locationManager startUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *lastLocation = [locations lastObject];
if (lastLocation.horizontalAccuracy > 10) {
return;
}
// setup a geocode fence and count as user enters fence
self.startRegion = [[CLCircularRegion alloc] initWithCenter:lastLocation.coordinate radius:10 identifier:#"startPosition"];
[self.locationManager stopUpdatingLocation];
[self.locationManager startMonitoringForRegion:self.startRegion];
}
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
lapCount++;
[self updateLapDisplay];
}
Am I doing something wrong or can I not use CoreLocation in such a fashion?
A geofence won't work well for this. You can't rely on it to trigger accurately or reliably. Use a location manager and its delegate in the normal way and trigger manually when the user comes to within 10 or 20 meters, then reset the trigger when they're 50 or 100 meters away from it.

Getting the current location only after the second time startUpdatingLocation is called

{
...
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];
}
}

Core Location not updating after accept prompt

I am using the Core Location framework to locate the device and once locationManager:didUpdateToLocation:fromLocation: method is called with a location, I stop tracking the user.
However the first time I launch the app (from a fresh install). When I message startUpdatingLocation of my Location Manager, the user gets the alert to accept or refuse location service.
When I accept the tracking doesn't begin, it's only when I go away and come back to this view controller when startUpdatingLocation is again called that notifications start coming in.
I am implementing locationManager:didChangeAuthorizationStatus: thinking that this would get messaged when the user accepts (or refuses) location services, but it doesn't.
Can anyone point me in the right direction for updating location as soon as the location services message has been dismissed ?
Thanks.
UPDATE WITH CODE SAMPLE
I've got a singleton class which encapsulates my logic, the idea is when the user location is requested, a check on the CLLocation's timestamp is performed and if it's too old, start tracking is messaged, which lazy loads my CLLocationManager iVar,
-(void)startTracking{
if(!self.locationManager)
_locationManager = [[CLLocationManager alloc] init];
if([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied){
[self invalidateUserLocation];
}else{
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
}
New location received:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
_userLocation = [newLocation retain];
NSDictionary * info = [NSDictionary dictionaryWithObject:self.userLocation
forKey:#"userLocation"];
[[NSNotificationCenter defaultCenter] postNotificationName:kUserLocationFound
object:self
userInfo:info];
[self stopTracking];
}
Stop tracking:
-(void)stopTracking{
if(!self.locationManager)
return;
[self.locationManager stopUpdatingLocation];
self.locationManager.delegate = nil;
}
When I have a view controller which needs the users location I call userLocation on my singleton object like so. If it's recent, I return the CLLocation, otherwise I return nil and start again. Notice I stop tracking when I receive the first update. But the first time this runs and I get the alert view, nothing is tracked at all.
- (CLLocation*)userLocation
{
if(_userLocation.coordinate.latitude == 0 && _userLocation.coordinate.longitude == 0){
[self startTracking];
return nil;
}else{
NSDate* timeNow = [NSDate date];
NSTimeInterval interval = [timeNow timeIntervalSinceDate:_userLocation.timestamp];
if(interval >10)
[self startTracking];
return _userLocation;
}
}
Did you try calling – locationManager:didChangeAuthorizationStatus: from CLLocationManagerDelegate?
I'm going to guess that you call startTracking when the view controller loads. This is circumvented by the alert which ask for if it's okay. At that point, the start locating message won't be called again so by calling didChangeAuthorizationStatus, you can call your startTracking method.
Something like:
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status == kCLAuthorizationStatusDenied) {
//location denied, handle accordingly
}
else if (status == kCLAuthorizationStatusAuthorized) {
//hooray! begin startTracking
}
}
If that's not the case, let me know.

Resources