Created
March 2, 2014 06:32
-
-
Save keicoder/9302772 to your computer and use it in GitHub Desktop.
objective-c : getting locaton
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//getting locaton | |
//1. MyLocations Project Settings | |
//use tab bar controller | |
//import CoreLocation.framework | |
//CurrentLocationViewController.h | |
#import <CoreLocation/CoreLocation.h> | |
@interface CurrentLocationViewController : UIViewController <CLLocationManagerDelegate> | |
@property (nonatomic, weak) IBOutlet UILabel *messageLabel; | |
@property (nonatomic, weak) IBOutlet UILabel *latitudeLabel; | |
@property (nonatomic, weak) IBOutlet UILabel *longitudeLabel; | |
@property (nonatomic, weak) IBOutlet UILabel *addressLabel; | |
@property (nonatomic, weak) IBOutlet UIButton *tagButton; | |
@property (nonatomic, weak) IBOutlet UIButton *getButton; | |
- (IBAction)getLocation:(id)sender; | |
@end | |
//CurrentLocationViewController.m | |
@implementation CurrentLocationViewController | |
{ | |
CLLocationManager *_locationManager; //CLLocationManager give you the GPS coordinates | |
} | |
- (id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
if ((self = [super initWithCoder:aDecoder])) { | |
_locationManager = [[CLLocationManager alloc] init]; | |
// For testing. Uncomment this line to use any location you want, | |
// without having to use the Simulator's Location menu. | |
//_location = [[CLLocation alloc] initWithLatitude:37.785834 longitude:-122.406417]; | |
} | |
return self; | |
} | |
//hooked up to the Get My Location button | |
- (IBAction)getLocation:(id)sender | |
{ | |
_locationManager.delegate = self; | |
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; //accuracy of up to ten meters | |
[_locationManager startUpdatingLocation]; ////Starts the generation of updates that report the user’s current location | |
} | |
#pragma mark - CLLocationManagerDelegate | |
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error | |
{ | |
NSLog(@"didFailWithError %@", error); | |
} | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
CLLocation *newLocation = [locations lastObject]; | |
NSLog(@"didUpdateLocations %@", newLocation); | |
} | |
//2. Putting the coordinates on the screen | |
//FirstViewController.m | |
@implementation CurrentLocationViewController | |
{ | |
CLLocationManager *_locationManager; | |
CLLocation *_location; //store the user’s current location in this variable | |
} | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
CLLocation *newLocation = [locations lastObject]; | |
NSLog(@"didUpdateLocations %@", newLocation); | |
_location = newLocation; | |
[self updateLabels]; | |
} | |
- (void)updateLabels | |
{ | |
if (_location != nil) { | |
self.latitudeLabel.text = [NSString stringWithFormat: | |
@"%.8f", _location.coordinate.latitude]; //.8 means 8 digits behind the decimal point | |
self.longitudeLabel.text = [NSString stringWithFormat: | |
@"%.8f", _location.coordinate.longitude]; | |
self.tagButton.hidden = NO; | |
self.messageLabel.text = @""; | |
} else { | |
// Initially, the screen should say, “Press the Button to Start” and the latitude and longitude labels are empty | |
self.latitudeLabel.text = @""; | |
self.longitudeLabel.text = @""; | |
self.addressLabel.text = @""; | |
self.tagButton.hidden = YES; | |
self.messageLabel.text = @"Press the Button to Start"; | |
} | |
} | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
[self updateLabels]; | |
} | |
//3. Handling errors | |
//CurrentLocationViewController.m | |
@implementation CurrentLocationViewController | |
{ | |
CLLocationManager *_locationManager; | |
CLLocation *_location; //store the user’s current location in this variable | |
BOOL _updatingLocation; //boolean, If it is NO, then location manager wasn’t currently active and there’s no need to stop it | |
NSError *_lastLocationError; | |
} | |
#pragma mark - CLLocationManagerDelegate | |
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error | |
{ | |
NSLog(@"didFailWithError %@", error); | |
// The kCLErrorLocationUnknown error means the location manager was unable | |
// to obtain a location right now. We will keep trying until we do find a | |
// location or receive a more serious error. | |
// Some of the Core Location errors: | |
// kCLErrorLocationUnknown - The location is currently unknown, but Core Location will keep trying. | |
// kCLErrorDenied - The user declined the app to use location services. | |
// kCLErrorNetwork - There was a network-related error | |
if (error.code == kCLErrorLocationUnknown) { | |
return; | |
} | |
[self stopLocationManager]; | |
_lastLocationError = error; | |
[self updateLabels]; | |
[self configureGetButton]; | |
} | |
- (void)stopLocationManager | |
{ | |
if (_updatingLocation) { | |
// Tell the location manager we no longer want to receive updates. | |
[_locationManager stopUpdatingLocation]; | |
_locationManager.delegate = nil; | |
_updatingLocation = NO; | |
} | |
} | |
- (void)updateLabels | |
{ | |
// If we have a location object then we will always show its coordinates, | |
// even if we're still fetching a more accurate location at the same time. | |
if (_location != nil) { | |
self.latitudeLabel.text = [NSString stringWithFormat:@"%.8f", _location.coordinate.latitude]; | |
self.longitudeLabel.text = [NSString stringWithFormat:@"%.8f", _location.coordinate.longitude]; | |
self.tagButton.hidden = NO; | |
self.messageLabel.text = @""; | |
// If we have no location yet, then we're either waiting for the user to | |
// press the button to start, still get our first location fix, or we ran | |
// into an error situation. | |
} else { | |
self.latitudeLabel.text = @""; | |
self.longitudeLabel.text = @""; | |
self.addressLabel.text = @""; | |
self.tagButton.hidden = YES; | |
//If the location manager gave an error, the label will show an error message | |
NSString *statusMessage; | |
if (_lastLocationError != nil) { | |
if ([_lastLocationError.domain isEqualToString:kCLErrorDomain] && _lastLocationError.code == kCLErrorDenied) { | |
statusMessage = @"Location Services Disabled"; | |
} else { | |
statusMessage = @"Error Getting Location"; | |
} | |
} else if (![CLLocationManager locationServicesEnabled]) { | |
statusMessage = @"Location Services Disabled"; | |
} else if (_updatingLocation) { | |
statusMessage = @"Searching..."; | |
} else { | |
statusMessage = @"Press the Button to Start"; | |
} | |
self.messageLabel.text = statusMessage; | |
} | |
} | |
//move getLocation action into its own startLocationManager method | |
- (void)startLocationManager | |
{ | |
if ([CLLocationManager locationServicesEnabled]) { //locationServicesEnabled : Returns a Boolean value indicating whether location services are enabled on the device. | |
// Tell the location manager to start fetching the location. | |
_locationManager.delegate = self; | |
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; | |
[_locationManager startUpdatingLocation]; ////Starts the generation of updates that report the user’s current location | |
_updatingLocation = YES; | |
} | |
} | |
//Change the getLocation method to | |
- (IBAction)getLocation:(id)sender | |
{ | |
[self startLocationManager]; | |
[self updateLabels]; | |
} | |
//change didUpdateLocations method | |
//in case there was an error and no location could be obtained | |
//but walk around for a bit and a valid location comes in | |
//in that case wipe the old error code | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
CLLocation *newLocation = [locations lastObject]; | |
NSLog(@"didUpdateLocations %@", newLocation); | |
_lastLocationError = nil; _location = newLocation; | |
[self updateLabels]; | |
} | |
//4. Improving the results | |
//CurrentLocationViewController.m | |
//stop getting location updates, when the location is accurate enough | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
CLLocation *newLocation = [locations lastObject]; | |
NSLog(@"didUpdateLocations %@", newLocation); | |
// If the time at which the new location object was determined is too long | |
// ago (5 seconds in this case), then this is a cached result. ignore | |
// these cached locations because they may be out of date. | |
if ([newLocation.timestamp timeIntervalSinceNow] < -5.0) { | |
//timestamp : The time at which location was determined | |
return; | |
} | |
// Ignore invalid measurements. | |
if (newLocation.horizontalAccuracy < 0) { | |
return; | |
} | |
// Only perform the following code if the new location provides a more | |
// precise reading than the previous one, or if it's the very first. | |
// _location is nil means this is the very first location update | |
if (_location == nil || _location.horizontalAccuracy > newLocation.horizontalAccuracy) { | |
// Put the new coordinates on the screen. | |
_lastLocationError = nil; | |
_location = newLocation; | |
[self updateLabels]; | |
// We're done if the new location is accurate enough. | |
if (newLocation.horizontalAccuracy <= _locationManager.desiredAccuracy) { | |
NSLog(@"*** We're done!"); | |
[self stopLocationManager]; | |
} | |
} | |
} | |
//change the state of button | |
- (void)configureGetButton | |
{ | |
if (_updatingLocation) { | |
[self.getButton setTitle:@"Stop" forState:UIControlStateNormal]; | |
} else { | |
[self.getButton setTitle:@"Get My Location" forState:UIControlStateNormal]; | |
} | |
} | |
//call the configureGetButton method | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
[self updateLabels]; | |
[self configureGetButton]; | |
} | |
- (IBAction)getLocation:(id)sender { | |
[self startLocationManager]; [self updateLabels]; | |
[self configureGetButton]; | |
} | |
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error | |
{ | |
//... | |
[self updateLabels]; | |
[self configureGetButton]; | |
} | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
//... | |
if (newLocation.horizontalAccuracy <= _locationManager.desiredAccuracy) { | |
NSLog(@"*** We're done!"); | |
[self stopLocationManager]; | |
[self configureGetButton]; | |
} | |
} | |
} | |
- (IBAction)getLocation:(id)sender | |
{ | |
if (_updatingLocation) { //_updatingLocation flag to determine what state the app is in | |
[self stopLocationManager]; | |
} else { | |
_location = nil; | |
_lastLocationError = nil; | |
_placemark = nil; | |
_lastGeocodingError = nil; | |
[self startLocationManager]; | |
} | |
[self updateLabels]; | |
[self configureGetButton]; | |
} | |
//5. Reverse Geocoding (using CLGeocoder) | |
//CLGeocoder object to turn the location data into a human-readable address | |
//CurrentLocationViewController.m | |
//add the following instance variables | |
@implementation CurrentLocationViewController | |
{ | |
CLGeocoder *_geocoder; | |
CLPlacemark *_placemark; //Placemark data includes information such as the country, state, city, and street address | |
BOOL _performingReverseGeocoding; | |
NSError *_lastGeocodingError; | |
} | |
//create the geocoder object | |
- (id)initWithCoder:(NSCoder *)aDecoder | |
{ | |
if ((self = [super initWithCoder:aDecoder])) { | |
//... | |
_geocoder = [[CLGeocoder alloc] init]; | |
} | |
return self; | |
} | |
//put the geocoder to work | |
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations | |
{ | |
//... | |
if (_location == nil || _location.horizontalAccuracy > newLocation.horizontalAccuracy) { | |
//... | |
if (newLocation.horizontalAccuracy <= _locationManager.desiredAccuracy) { | |
//... | |
if (!_performingReverseGeocoding) { | |
NSLog(@"*** Going to geocode"); | |
// Unlike the location manager, CLGeocoder does not use a delegate to tell you about the result, | |
// but something called a block. | |
// Start a new reverse geocoding request and update the screen | |
// with the results (a new placemark or error message). | |
_performingReverseGeocoding = YES; | |
[_geocoder reverseGeocodeLocation:_location completionHandler:^(NSArray *placemarks, NSError *error) { | |
NSLog(@"*** Found placemarks: %@, error: %@", placemarks, error); | |
_lastGeocodingError = error; | |
if (error == nil && [placemarks count] > 0) { | |
_placemark = [placemarks lastObject]; //Placemark data includes information such as the country, state, city, and street address | |
} else { | |
_placemark = nil; | |
} | |
_performingReverseGeocoding = NO; | |
[self updateLabels]; | |
}]; | |
} | |
} | |
} | |
//make the address visible to the user | |
//Change updateLabels | |
- (void)updateLabels | |
{ | |
// If we have a location object then we will always show its coordinates, | |
// even if we're still fetching a more accurate location at the same time. | |
if (_location != nil) { | |
//... | |
// Once we have a location, we try to reverse geocode it and show the | |
// results in the address label. | |
if (_placemark != nil) { | |
self.addressLabel.text = [self stringFromPlacemark:_placemark]; | |
} else if (_performingReverseGeocoding) { | |
self.addressLabel.text = @"Searching for Address..."; | |
} else if (_lastGeocodingError != nil) { | |
self.addressLabel.text = @"Error Finding Address"; | |
} else { | |
self.addressLabel.text = @"No Address Found"; | |
} | |
// If we have no location yet, then we're either waiting for the user to | |
// press the button to start, still get our first location fix, or we ran | |
// into an error situation. | |
} else { | |
//... | |
} | |
} | |
//Add the stringFromPlacemark: | |
- (NSString *)stringFromPlacemark:(CLPlacemark *)thePlacemark | |
{ | |
return [NSString stringWithFormat:@"%@ %@\n%@ %@ %@", | |
thePlacemark.subThoroughfare, thePlacemark.thoroughfare, | |
thePlacemark.locality, thePlacemark.administrativeArea, | |
thePlacemark.postalCode]; | |
//subThoroughfare: Additional street-level information for the placemark | |
//thoroughfare: The street address associated with the placemark | |
//locality: The city associated with the placemark | |
//administrativeArea: The state or province associated with the placemark | |
//postalCode: The postal code associated with the placemark | |
} | |
//in getLocation, clear out the _placemark and _lastGeocodingError variables | |
//in order to start with a clean slate | |
- (IBAction)getLocation:(id)sender | |
{ | |
if (_updatingLocation) { | |
[self stopLocationManager]; | |
} else { | |
_location = nil; | |
_lastLocationError = nil; | |
_placemark = nil; | |
_lastGeocodingError = nil; | |
[self startLocationManager]; | |
} | |
[self updateLabels]; | |
[self configureGetButton]; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment