Reputation: 5524
I want my app to get current location when user taps a button. I initialize locationManager
object in init method.
First question: is this good if I'm going to need currentlocation every time I press button? Or should I initialize it even in viewDidLoad
?
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Create location manager object
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
// Best accuracy as posibble
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
}
return self;
}
In my delegate method I stopUpdatingLocation
as soon as I got my currentLocation
.
// Delegate method
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray
*)locations
{
CLLocation *currentLocation = [locations lastObject];
[locationManager stopUpdatingLocation];
}
Here I startUpdatingLocation
with button:
- (IBAction)getCurrentLocation:(id)sender
{
[locationManager startUpdatingLocation];
}
Second question is: when I press button 1st time, I get right location, but when I change location in simulator and press again it shows the 1st one. Again, when I press the button, then it shows the right one. I have tried to initialize locationManager
every time button is pressed but it doesn't work that way neither.
I read on other post that its because of location cache or something like that. How do I remove that cache ? Because I'm going to store that location somewhere else in DB anyways so I don't need old one..
Upvotes: 0
Views: 2181
Reputation: 12625
If you just need one update for the current location per button press, why not create a singleton class (iOS 8 based example shown below), and cache the current location, then add a 're-trigger' method that's invoked when the user presses a button that tells the location manager to start updating the location again, cache the results and then switch off the updating (again)? That way you won't waste resources getting more updates than necessary. You'll still have to somehow synchronize the presentation so it's obvious or handled when a location isn't available yet or current.
import Foundation
import CoreLocation
class CurrentLocation : NSObject, CLLocationManagerDelegate {
enum InitPhase {
case RequestingAuthorization, StartUpdatingLocation, ReverseGeocoding
}
var locationManager : CLLocationManager?
var placemark : CLPlacemark?
var initializationPhase : InitPhase = .RequestingAuthorization
override init() {
super.init()
placemark = nil
locationManager = CLLocationManager()
locationManager!.delegate = self
locationManager!.requestWhenInUseAuthorization()
}
func nonull(s : String?) -> String {
if (s == nil) {
return ""
}
return s!
}
// Handle all NSErrors here (not just from location manager)
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
switch(initializationPhase) {
case .RequestingAuthorization:
println("CurrentLocation class - App not authorized by user to use location services")
case .StartUpdatingLocation:
println("CurrentLocation class - StartUpdatingLocation\n ClLocationManager reported error to delegate:\n \(error.localizedDescription)")
case .ReverseGeocoding:
println("CurrentLocation class - ReverseGeocoding\n CLGeocoder().reverseGeocodeLocation() error:\n \(error.localizedDescription)")
default:
println("CurrentLocation class - Unknown basis\n ClLocationManager reported error to delegate:\n \(error.localizedDescription)")
}
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedAlways || status == .AuthorizedWhenInUse) {
println("CurrentLocation class - App authorized to use location services")
initializationPhase = .StartUpdatingLocation
locationManager!.startUpdatingLocation()
} else {
println("CurrentLocation class - App not authorized by user to use location services")
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var location = locations.last as CLLocation
println("CurrentLocation class - CLLocation delagate received loocation update\n")
locationManager?.stopUpdatingLocation()
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in
if (error == nil) {
println(" Latitude/Longitude: \(location.coordinate.latitude) \(location.coordinate.longitude)")
if placemarks.count > 0 {
self.placemark = CLPlacemark(placemark: placemarks[0] as CLPlacemark)
println(String(format:" subThoroughfare: %@\n thoroughfare: %@\n locality: %@\n postalCode: %@\n administrativeArea: %@\n country: %@",
self.nonull(self.placemark!.subThoroughfare),
self.nonull(self.placemark!.thoroughfare),
self.nonull(self.placemark!.locality),
self.nonull(self.placemark!.postalCode),
self.nonull(self.placemark!.administrativeArea),
self.nonull(self.placemark!.country)))
} else {
println("No placemarks located")
}
} else {
self.locationManager(manager, didFailWithError: error)
}
})
}
}
Upvotes: 0
Reputation: 3167
init
or in viewDidLoad
I would go with viewDidLoad
, only because that way you can be sure that all dependencies have been loaded.
The user location isn't changing because you stop listening to location updates after you receive location information for the first time:
[locationManager stopUpdatingLocation];
But then you start listening again when the user presses the getCurrentLocation
button.
You can still go with this approach, if you like, but I would set a flag in the getCurrentLocation
action to update the UI when a new location is found.
Example:
// Delegate method
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray
*)locations
{
CLLocation *currentLocation = [locations lastObject];
[locationManager stopUpdatingLocation];
if (self.updateUIOnNextLocation) {
[self updateUIWithLocation:currentLocation];
self.updateUIOnNextLocation = NO;
}
}
...
- (IBAction)getCurrentLocation:(id)sender
{
self.updateUIOnNextLocation = YES;
[locationManager startUpdatingLocation];
}
Upvotes: 1