I'm trying to implement a function of a Geo location for a user, I'm statically setting up latitude and longitude information, when app starts if the user is within that area I'm showing up a message that "You've been reached to office" else "You're going out from office". I've implemented below code to achieve this, I tried by moving all around by steps and on vehicles, but in both the cases it always shows that "You've been reached to office", however I was 2km away from that location! I think the problem is in the comparison of Geo data in CLLocationManager delegate.
- (void) startUpdateUserLocation
{
if(!locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
// [locationManager startUpdatingLocation];
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(latitude, longitude);
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:coord radius:kCLDistanceFilterNone identifier:#"identifier"];
[locationManager startMonitoringForRegion:region];
}
- (void)viewDidLoad
{
[super viewDidLoad];
latitude = 23.076289;
longitude = 72.508129;
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
MKCoordinateRegion region;
region.center = mapView.userLocation.coordinate;
region.span = MKCoordinateSpanMake(0.25, 0.25);
region = [mapView regionThatFits:region];
[mapView setRegion:region animated:YES];
lblCurrentCoords.text = [NSString stringWithFormat:#"lat %f lon %f",mapView.userLocation.coordinate.latitude,mapView.userLocation.coordinate.longitude];
[self startUpdateUserLocation];
}
- (void)locationManager:(CLLocationManager *)manager
didEnterRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
[listOfPoints addObject:manager.location];
[tablePoints reloadData];
/*
* locationManager:didEnterRegion:
*
* Discussion:
* Invoked when the user enters a monitored region. This callback will be invoked for every allocated
* CLLocationManager instance with a non-nil delegate that implements this method.
*/
lblLocationStatus.text = #"You're in office area!...";
}
- (void)locationManager:(CLLocationManager *)manager
didExitRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
/*
* locationManager:didExitRegion:
*
* Discussion:
* Invoked when the user exits a monitored region. This callback will be invoked for every allocated
* CLLocationManager instance with a non-nil delegate that implements this method.
*/
lblLocationStatus.text = #"You're going out from office area!...";
}
- (void)locationManager:(CLLocationManager *)manager
didStartMonitoringForRegion:(CLRegion *)region __OSX_AVAILABLE_STARTING(__MAC_TBD,__IPHONE_5_0)
{
/*
* locationManager:didStartMonitoringForRegion:
*
* Discussion:
* Invoked when a monitoring for a region started successfully.
*/
lblLocationStatus.text = #"Start monitoring...";
}
- (void)locationManager:(CLLocationManager *)manager
monitoringDidFailForRegion:(CLRegion *)region
withError:(NSError *)error __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0)
{
/*
* locationManager:monitoringDidFailForRegion:withError:
*
* Discussion:
* Invoked when a region monitoring error has occurred. Error types are defined in "CLError.h".
*/
lblLocationStatus.text = #"Stop monitoring...";
}
I am trying to accomplish the following things!
If the user entered into the Geo location, he should be "alert". --- How to match location?
If moving around within that Geo location then the code should be monitoring this activity! --- Need to set a desired accuracy property?
I want my code to check constantly for the user Geo location, how do I do that? --- Need to call function in NSTimer ?
I found many questions on SO asked about the same but NO one has matched answers! Someone please guide me whether I'm going in the right direction or not as this code doesn't show up! :)
It sounds like you should be using region monitoring instead, which tells you when the user enters or exits a circular area. Set it up with startMonitoringForRegion: and implement the CLLocationManagerDelegate methods
– locationManager:didEnterRegion:
– locationManager:didExitRegion:
– locationManager:monitoringDidFailForRegion:withError:
– locationManager:didStartMonitoringForRegion:
If you're having trouble with bad location data coming in, check for the age of the CLLocation in locationManager:didUpdateLocations: or locationManager:didUpdateToLocation:fromLocation:. If it's more than 60 seconds old, don't use it.
Related
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;
}
I have IBAction:
-(IBAction)pressStart{
locationManager.delegate = self;
[locationManager startUpdatingLocation];
}
In
- (void)viewDidLoad
{
[super viewDidLoad];
duration.text = #"00:00:00";
speedLabel.text = #"00";
locationManager = [[CLLocationManager alloc]init];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
}
And this method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *firstLocation = [locations objectAtIndex:0];
location = [locations lastObject];
CLLocationDistance meters = [location distanceFromLocation:firstLocation];
NSLog(#"meters= %f", meters);
And I don't know why the firstLocation is changing. Maybe there is a way to capture firstLocation? This should be the location of the device when the button Start is pressed.
firstLocation in your code is not the first location since location updates started; it is the first location to be returned to you since the previous callback to the locationManager:didUpdateLocations: method (the location services may collect multiple locations before calling back to your delegate method in certain circumstances -- the most recent location is always going to be the last object in the locations array).
If you need to store the first location since location updates started, you should create a property such as
#property (nonatomic, strong) CLLocation *startingLocation;
Then in the locationManager:didUpdateLocations: method, add the code:
if (!self.startingLocation) {
self.startingLocation = [locations objectAtIndex:0];
}
That will store the starting location into the property after the first callback. (You can set the property to nil if you want to reset it.)
Don't forget that the very first location you receive many not be very accurate, as it takes time for location services to get a fix on the device's position if they were not recently enabled.
I init my mapviewControler like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstShow=TRUE;
self.mapView.delegate=self;
self.mapView.showsUserLocation=YES;
self.cllmng=[[CLLocationManager alloc]init];
self.cllmng.delegate=self;
self.cllmng.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
self.cllmng.distanceFilter=50;
[self.cllmng startUpdatingLocation];
}
and I make mapviewControler implement CLLocationManagerDelegate, and implement - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations in my code.
The problem I have is that I want to init map view centred on current user location with a proper scale. I intend to do this by
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation * currentLoci=[locations lastObject];
MKCoordinateRegion r;
MKCoordinateSpan span;
span.latitudeDelta = 1;
span.longitudeDelta = 1;
r.span = span;
CLLocationCoordinate2D c;
c.longitude=currentLoci.coordinate.longitude;
c.latitude=currentLoci.coordinate.latitude;
r.center = c;
[self.mapView setRegion:r animated:YES];
}
But sometimes, after [self.cllmng startUpdatingLocation] called in the -(void) viewDidload, I cannot get a call of - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations immediately. Thus, the map view just init showing user location but showing the whole Australia. How can I set the scale/span in the init of map view even didUpdateLocations is not triggered? THX!
INstead of modifying the region yourself using a CLLocation manager you could just set the trackingMode of the mapView and it'll center and zoom in on your position automatically. It will be possible for the user to disable the tracking mode if they start dragging the map around, but you can disable user interaction if you really want them to have no control. Here is how to do it
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
ref: https://developer.apple.com/library/ios/documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html#//apple_ref/occ/instm/MKMapView/setUserTrackingMode:animated:
iOS 11.x Swift 4.0 Craigs answer, updated.
mapView.showsUserLocation = true
Not so obvious I fear.
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.
I have a MKMapView on my app. This is iOS6.
-(void)viewDidLoad
{
.....
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"Update locations is hit");
NSLog(#"379 the locations count is %d",locations.count);
CLLocation *obj = [locations lastObject];
NSLog(#"the lat is %f", obj.coordinate.latitude);
NSLog(#"the long is %f", obj.coordinate.longitude);
NSLog(#"the horizontal accuracy is %f",obj.horizontalAccuracy);
NSLog(#"the vertical accuracty is %f",obj.verticalAccuracy);
if (obj.coordinate.latitude != 0 && obj.coordinate.longitude != 0)
{
CLLocationCoordinate2D currrentCoordinates ;
currrentCoordinates.latitude = obj.coordinate.latitude;
currrentCoordinates.longitude = obj.coordinate.longitude;
}
....more computation
[locationManager stopUpdatingLocation];
}
When I first load the app, my location is showing little far away. Some times miles away. I also have a reset location button and if I click that map shows correct location. This is what I have in reset location button click:
- (IBAction)btnResetLocationClick:(UIButton *)sender
{
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
[locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager startUpdatingLocation];
}
So how do I make the app get the correct current location on load up itself. Is there a way for the app to tell the map to wait for few milliseconds and then update. Or any other idea? Please let me know. If you need more information, please ask. Thanks.
What you could do is to:
do not turn off location services in didUpdateLocations automatically, but rather;
turn off location services in didUpdateLocations only if you're sufficiently happy with the horizontalAccuracy; and
even if you don't get the desired accuracy, turn off location services after a certain amount of time has passed.
Thus, didUpdateLocations might look like:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
// do whatever you want with the location
// finally turn off location services if we're close enough
//
// I'm using 100m; maybe that's too far for you, but 5m is probably too small
// as you frequently never get that accurate of a location
if (location.horizontalAccuracy > 0 && location.horizontalAccuracy < 100)
[locationManager stopUpdatingLocation];
}
And then in viewDidLoad, turn if off after a certain period of time has passed (you might want to check some status variable that you set if you've already turned off location services):
-(void)viewDidLoad
{
.....
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager startUpdatingLocation];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 60.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[locationManager stopUpdatingLocation];
});
}
Original answer:
I don't see where you're updating your map to be around your location. I'd expect to see something like:
self.mapView.centerCoordinate = location.coordinate;
or like:
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(location.coordinate, 300, 300);
[self.mapView setRegion:region];
I'd also suggest, rather than turning off location services immediately (since frequently the first few locations are not that accurate), leave it on for a bit and let it hone in on your location until the horizontalAccuracy and verticalAccuracy fall within a certain predetermined limit. Look at those accuracy figures for a few calls to didUpdateLocations and you'll see what I mean.
I originally thought you were getting a negative horizontalAccuracy at which point I suggested implementing didFailToLocateUserWithError because according to horizontalAccuracy, "A negative value indicates that the location’s latitude and longitude are invalid." Hopefully you get an error that describes what the issue is. Even if you're not currently getting a negative horizontalAccuracy, you might want to implement this method, just to make sure you're handling any errors correctly.
You can't make the GPS in the iPhone more accurate in your app, but you can check that the result is accurate before carrying on. Right now you're only checking the lat and long aren't 0, but if you check obj's horizontalAccuracy then you'll know when the location information is good enough. Don't stopUpdatingLoation until that happens.