Reputation: 12051
What I want to accomplish in my app is to get the current user location and display it onscreen in a UILabel
. I would like to have an NSString
of current user's location with a format similar to this: @"City, State/Country"
. It would be a one-time operation at the start of the app launch.
I have no prior experience with location in iOS and I would like to get some advice on this one - I'm sure it's quite a simple task.
Upvotes: 1
Views: 2221
Reputation: 438467
The process is as follows:
Add CoreLocation.framework
to your project. See Linking to a Library or a Framework. If you want to use the address book constants that I use below, you might want to add the AddressBook.framework
to your project, too.
Start location services. For this purpose, the "significant change" service (less accurate, but lower power consumption) is probably sufficient for city-level accuracy.
When the location manager informs you of the user's location, then perform a reverse geocode of that location.
Stop location services.
Thus, that might look like:
#import <CoreLocation/CoreLocation.h>
#import <AddressBook/AddressBook.h>
@interface ViewController () <CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self startSignificantChangeUpdates];
}
- (void)startSignificantChangeUpdates
{
if ([CLLocationManager locationServicesEnabled])
{
if (!self.locationManager)
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
}
- (void)stopSignificantChangesUpdates
{
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *placemark = placemarks[0];
NSDictionary *addressDictionary = [placemark addressDictionary];
NSString *city = addressDictionary[(NSString *)kABPersonAddressCityKey];
NSString *state = addressDictionary[(NSString *)kABPersonAddressStateKey];
NSString *country = placemark.country;
self.label.text = [NSString stringWithFormat:@"%@, %@, %@", city, state, country];
}];
[self stopSignificantChangesUpdates];
}
Note, the location manager's notification of the location is contingent upon the user electing to share that with your app and will happen, even in the best case scenario, asynchronously. Likewise the reverse geocode happens asynchronously.
See Getting User Location from the Location Awareness Programming Guide.
Upvotes: 8
Reputation: 23359
Use -reverseGeocodeLocation:completionHandler:
of CLGeocoder
.
Try this code snippet, the only trick is that the CLPlacemark
(see the Documentation for available info) you get back from the Geocoder has a bunch of info which isn't always consistent, this was one of my tries from an older project, trying to test for location, street name etc... test with your usage case to find a good match:
- (void)getLocationStringForCoordinates:(CLLocationCoordinate2D)coordinates {
if ( CLLocationCoordinate2DIsValid(coordinates) ) {
CLLocation *photoLocation = [[CLLocation alloc] initWithLatitude:coordinates.latitude longitude:coordinates.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:photoLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
CLPlacemark *locationPlacemark = [placemarks lastObject];
// Location (popular name, street, area)
NSString *location = locationPlacemark.subLocality ? locationPlacemark.subLocality : (locationPlacemark.name ? locationPlacemark.name : locationPlacemark.thoroughfare);
// sometimes the location can be the same
// as the city name (for small villages), if so
// make sure location is nil to skip it
// else if
// the location name is not being used but is very short 9less then 20 letters, use that instead
if([locationPlacemark.name isEqualToString:locationPlacemark.locality] && [location isEqualToString:locationPlacemark.name])
location = @"";
else if ( ![locationPlacemark.name isEqualToString:location] && locationPlacemark.name.length < 20 )
location = locationPlacemark.name;
// city
NSString *city = locationPlacemark.subAdministrativeArea ? locationPlacemark.subAdministrativeArea : locationPlacemark.locality;
city = city.length > 0 ? [@", " stringByAppendingString:city] : city;
NSString *locationName = [NSString stringWithFormat:@"%@%@", location, city];
}];
}
}
Upvotes: 2
Reputation: 1625
Take a look at the reverseGeocodeLocation:completionHandler: method for CLGeocoder:
First you will have to use a CLLocationManager to get a CLLocation representing the user's current position.
Upvotes: 0
Reputation: 12051
I've found a really nice and simple to follow tutorial on this topic - http://www.appcoda.com/how-to-get-current-location-iphone-user/
Hope it will be helpful to others!
Upvotes: 0