I wrote a simple iOS application that retrieves location information and then uses the location to request Yahoo Weather.
The problem is that even when I call the Core Location in the viewDidLoad, it won't give me the result immediately.
So why can't I get the location information?
How can I get the location information in viewDidLoad?
The pseudocode currently is something like:
- (void)viewDidLoad
{
[super viewDidLoad];
self.locManager = [[CLLocationManager alloc] init];
self.locManager.delegate = self;
self.locManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locManager.distanceFilter = 100;
[self.locManager startUpdatingLocation];
//won't get the current location right now, so the output will be null
NSLog(#"Current Location Longitude: %#", self.longitudeString);
NSLog(#"Current Location Latitude: %#", self.latitudeString);
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = [locations lastObject];
self.longitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
self.latitudeString = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
Location updates are not provided as instantly as you are expecting, you need to wait few seconds (2-3 or may be more) to get precise location update. If you want to have location data in viewDidLoad then you should init your location manager (and call startUpdatingLocation) before invoking the ViewController (since then it is not guaranteed that you will have location-data in viewDidLoad).
Related
Can I detect whether a user moved to another country?
(Not using Locale.current)
The location detection should be running in background.
I'm hoping to do something like this.
Eg. A user from US leaves the country to UK. Then, when the user reach UK, i am able to detect it at the background and send notification.
You should in your Info.plist, set allowsBackgroundLocationUpdates to YES, this you can search google, and lots of answer for adapt iOS 9.
First you can use CLLocationManager to get the location:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib .
//delegate
self.locationManager.delegate = self;
//The desired location accuracy.
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//Specifies the minimum update distance in meters.
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.purpose = #"To provide functionality based on user's current location.";
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
UIAlertView* av = [[UIAlertView alloc] initWithTitle:#"update" message:[NSString stringWithFormat:#"didUpdateToLocation: newLocation: %# old:%#",newLocation,oldLocation] delegate:nil cancelButtonTitle:#"cancel" otherButtonTitles:#"ok", nil nil];
[av show];
}
Secondly, you can use CLGeocoder to get country or city.
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// get city name
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *array, NSError *error)
{
if (array.count > 0)
{
CLPlacemark *placemark = [array objectAtIndex:0];
NSString *city = placemark.locality;
}
else if (error == nil && [array count] == 0)
{
NSLog(#"No results were returned.");
}
else if (error != nil)
{
NSLog(#"An error occurred = %#", error);
}
}];
}
You can give a duration to get location per duration:
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
newLocation = [locations lastObject];
double lat = newLocation.coordinate.latitude;
double lon = newLocation.coordinate.longitude;
NSLog(#"lat:%f,lon:%f",lat,lon);
if (!self.deferringUpdates) {
CLLocationDistance distance = 500;
NSTimeInterval time = 20;
[locationManager allowDeferredLocationUpdatesUntilTraveled:distance
timeout:time];
self.deferringUpdates = YES;
}
}
You can create UNNotificationRequest with exit UNLocationNotificationTrigger.
UNNotificationRequest
A UNNotificationRequest object is used to schedule a local notification and manages the content for a delivered notification. A notification request object contains a UNNotificationContent object with the contents of the notification. It also contains the UNNotificationTrigger object that specifies the conditions that trigger the delivery of the notification. For a delivered notification, you use these objects to fetch information about the notification.
UNLocationNotificationTrigger
A UNLocationNotificationTrigger object causes the delivery of a notification when the device enters or leaves a specified geographic region. Use this object to specify the region information needed to trigger the notification. Location triggers can fire once or they can fire multiple times.
Apps must request access to location services and must have when-in-use permissions to use this class. To request permission to use location services, call the requestWhenInUseAuthorization() method of CLLocationManager before scheduling any location-based triggers.
Flow
Each time user opens app, check his local country and define location trigger
let region: CLRegion = <your code defining country region>
region.notifyOnEntry = false
region.notifyOnExit = true
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
and using that trigger reschedule notification request (UNNotificationRequest).
When trigger fires (user leaves region) — app will present local notification, and if user taps on it, app starts, and if you add handler on local notification open you can notify your server about user moving away and check his new country and do what you need to do.
In my current project.
I need user's location at every 50 meter user move.
So Basically After open application every 50 meter change I need user location for call web service in Objective c. Also i want same process run when application is in background state.
Thanks in advance
You have to make object of CLLocationManager when application starts and set it's delegate
Add the below code to get user's current location
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
Now add the delegate of CLLocationManagaer that is didUpdateToLocation and add the following code in that.
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
if(meters==50)
{
// CALL YOU WEBSERVICE
}
set your location track in
//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];
and add
if ([CLLocationManager locationServicesEnabled]) {
[self.locationManager startUpdatingLocation];
}
Update
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *location = locations.lastObject;
NSLog(#"%#", location.description);
//In here you get all details like
NSLog(#"latitude = %#",location.coordinate.latitude);
NSLog(#"longitude = %#",location.coordinate.longitude);
NSLog(#"altitude = %#",location.altitude);
NSLog(#"horizontalAccuracy = %#",location.horizontalAccuracy);
NSLog(#"verticalAccuracy = %#",location.verticalAccuracy);
NSLog(#"timestamp = %#",location.timestamp);
NSLog(#"speed = %#",location.speed);
NSLog(#"course = %#",location.course);
}
So it works when I'm on WiFi. But on 4G, it only works if I had been on Wifi and it already has the location. A lot of times without WiFi, the phone will say it's using my location but its not updating the label nor is it uploading the coordinates to the server. Here's the code:
- (void)viewDidLoad {
[super viewDidLoad];
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
if ([self->locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self->locationManager requestWhenInUseAuthorization];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *newLocation = [locations lastObject];
NSString *locationLat = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.latitude];
NSString *locationLong = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.longitude];
latLongLabel.text = [NSString stringWithFormat:#"Lat: %# - Long%#",locationLat,locationLong];
[self postLocation:locationLat secondArg:locationLong];
}
- (void)postLocation: (NSString *)latitudeString secondArg:(NSString *)longitudeString {
//POST COORDINATES TO MY SERVER
}
- (IBAction)startUpdating:(id)sender {
[locationManager startUpdatingLocation];
}
My suspicion is that when you are on 4G, the location updates still work fine (although wifi triangulation makes them a bit more precise but only Apple knows how, as the implementation is private), BUT there might be an issue sending those values to the server quickly or reliably enough via 4G connection. (for example in London it is slow as hell with so many people around)
You might narrow the debugging by simply logging the location update directly to some UIlabel on your view, and not going through server infrastructure.
AS a last resort I would make sure
that you set your CLActivityType property to CLActivityTypeFitness
and pausesLocationUpdatesAutomatically is set to NO.
I'm trying to develop an Altimeter on Xcode and Simulator but it always return 0 for the height above the sea.
I don't understand why, I've tried with a lot of places on Simulator.
My code is this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_mapView.delegate = self;
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.pausesLocationUpdatesAutomatically = YES;
_locationManager.delegate = self;
firstLocation = YES;
checkUserLocation = NO;
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
CLLocation *location = [locations lastObject];
lastLocation = location.coordinate;
_latitudeLabel.text = [NSString stringWithFormat:#"Current latitude: %f", location.coordinate.latitude];
_longitudeLabel.text = [NSString stringWithFormat:#"Current longitude: %f", location.coordinate.longitude];
_altitudeLabel.text = [NSString stringWithFormat:#"Current altitude: %f m", location.altitude];
}
Where is my error?
There's nothing wrong with your code, its because you are using the simulator.
Determining altitude requires a device with GPS capabilities, and you also need to be using GPS on that device in order to get it (wifi only location would not report altitude correctly even on GPS-enabled devices). The iOS simulator does not have those capabilities so altitude will not be accurate there. You will need to use a real device with GPS to get altitude measurements.
If you want to simulate a CLLocation with altitude you can create your CLLocation object and pass it an altitude yourself:
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate
altitude:(CLLocationDistance)altitude
horizontalAccuracy:(CLLocationAccuracy)hAccuracy
verticalAccuracy:(CLLocationAccuracy)vAccuracy
timestamp:(NSDate *)timestamp
Altitude is a readonly property so you will need to create a new CLLocation object yourself instead of changing it manually when you receive a CLLocation object in your delegate callback.
I have an application which creates a class instance that contains (amongst other things) some location data.
In the app delegate I set up location services and start grabbing the location data;
//Delegate method to receive location information from locationManager
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
latestLocation = newLocation;//Make latest location the same as NewLocation
NSLog(#"Location is: %#", latestLocation);
}
I declare latest location as a property so that I can get at the CLLocation instance from another class.
My captures class, when called grabs the CLLocation when its init method is called;
//Designated initialiser
-(id) initWithVideoPath:(NSString *) vPath
userNotes:(NSString *) uNotes
retentionState:(NSString *) rState
{
//Call the super classes designated initializer
[super init];
//Get a pointer to the application delegate so we can access the location props
Rolling_VideoAppDelegate *appDelegate = (Rolling_VideoAppDelegate*)[UIApplication sharedApplication].delegate;
//If superclass failed to init
if (!self)
return nil;
//Give the variables some initial values
[self setVideoPath:vPath];
[self setUserNotes:uNotes];
[self setRetentionState:rState];
dateCreated = [[NSDate alloc] init];
mp = [[MapPoint alloc]initWithCoordinate:[[appDelegate latestLocation]coordinate]];//get the location from the coords from appDelegate
return self;
[dateCreated release];
}
However the app crashes when the mapPoint init is called. The problem is I'm not getting the CLLocation information in properly.
I'm still unsure why the original solution does not work, so if anyone has any insights please do enlighten.
I have however worked out a slightly inelegant work around using NSUserDefaults
latestLocation = newLocation;//Make latest location the same as NewLocation
//Use NSUser Defaults to save the CLLocation instance
NSUserDefaults *location = [NSUserDefaults standardUserDefaults];
[location setDouble:latestLocation.coordinate.latitude forKey:#"lat"];
[location setDouble:latestLocation.coordinate.longitude forKey:#"longd"];
I needed to break out the lat and long as NSUserDefaults will not store CLLocation objects (NSCoding compatability), the reconstruct them in the captures class;
NSUserDefaults *location = [NSUserDefaults standardUserDefaults];//Get a handle to the user defaults
CLLocationDegrees lat = [location doubleForKey:#"lat"];
CLLocationDegrees longd = [location doubleForKey:#"longd"];
CLLocation *currentLocation = [[CLLocation alloc] initWithLatitude:lat longitude:longd];
mp = [[MapPoint alloc]initWithCoordinate:[currentLocation coordinate]];//get the location from the coords from appDelegate