4

I have a view controller that pulls the users latitude and longitude coordinates from the app delegate. This works well, but I also need the user's city, state, and time zone. I know I should use CLGeocoder for this (please see last chunk of code), but don't know how to put it together. I'd just need NSStrings of the city, state, and timezone. Anyone have any pointers or an example? Thank you!

In my App Delegate, I use CCLocationManager to get the Coordinates like this:

- (NSString *)getUserCoordinates
{
    NSString *userCoordinates = [NSString stringWithFormat:@"latitude: %f longitude: %f", 
    locationManager.location.coordinate.latitude, 
    locationManager.location.coordinate.longitude];
    locationManager = [[CLLocationManager alloc] init];
    locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
    [locationManager startUpdatingLocation];
    return userCoordinates;
}

- (NSString *)getUserLatitude
{
    NSString *userLatitude = [NSString stringWithFormat:@"%f", 
    locationManager.location.coordinate.latitude];
    return userLatitude;
}

- (NSString *)getUserLongitude
{
    NSString *userLongitude = [NSString stringWithFormat:@"%f", 
    locationManager.location.coordinate.longitude];
    return userLongitude;
}

In my View Controller, I get the user's Latitude and Longitude as an NSString with this:

NSString *userLatitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate 
getUserLatitude];

NSString *userLongitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate 
getUserLongitude];

I would like to get the city, state, and timezone. I understand I need CLGeocoder, but can't figure out how to meld it together:

CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, 
NSError *error) {
    for (CLPlacemark * placemark in placemarks) {
        NSString *locality = [placemark locality];
    }
}
Brandon
  • 2,163
  • 6
  • 40
  • 64

4 Answers4

12

A couple things, Brandon:

1) CLLocationManager might not give you an instant response to your request for coordinates. You should set your view controller as a CLLocationManager delegate and then when the location update comes in (which will be in the locationManager:didUpdateLocations: method), then you can run your CLGeocoder method.

2)

Which I wrote to look like this:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    NSLog( @"didUpdateLocation!");
    NSLog( @"latitude is %@ and longitude is %@", [self getUserLatitude], [self getUserLongitude]);

    CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
    [geoCoder reverseGeocodeLocation:locationManager.location completionHandler:^(NSArray *placemarks, NSError *error) {
        for (CLPlacemark * placemark in placemarks) {
            NSString * addressName = [placemark name];
            NSString * city = [placemark locality]; // locality means "city"
            NSString * administrativeArea = [placemark administrativeArea]; // which is "state" in the U.S.A.

            NSLog( @"name is %@ and locality is %@ and administrative area is %@", addressName, city, administrativeArea );
        }
    }];
}

Getting the location's timezone is a bit trickier. I bet there's an API or some sample code to get it within iOS, but it's not a part of the CLPlacemark API.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • Best place to put this probably would be in your view controller. But be careful, if your view controller isn't visible or, more importantly, doesn't exist, make sure to set the location manager delegate to null so you don't get any unexpected bad behavior. – Michael Dautermann Mar 09 '13 at 07:35
  • OK Michael - think I got it. Thank you! – Brandon Mar 09 '13 at 07:38
  • Hi Michael - Im actually getting 2 errors - "no visible interface... declares the selector getUserLatitude" and "use of undeclared identifier locationManager" – Brandon Mar 09 '13 at 07:41
2

Form a CLLocation from latitude and longitude double value. Then feed that location to reverseGeocodeLocation:completionHandler:

Also note that the method reverseGeocodeLocation:completionHandler: is asynchronous.

You can also use, CLLocationManagerDelegate's locationManager:didUpdateHeading: to asynchronously update if there is an Location available, which is better.

Anyway following your approach, just modifying some of your code from AppDelegate

- (double)getUserLatitude
{
    return retrun locationManager.location.coordinate.latitude;
}

- (double)getUserLongitude
{
    retrun locationManager.location.coordinate.longitude;
}

-(CLLocationManager*) getLocationManager
{
    return locationManager;
}

Now Form a Location object

double latt = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getUserLatitude];

double longt = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getUserLongitude];
CLLocation loc = [[CLLocation alloc] initWithLatitude:latt longitude:longt]

or you can directly get the location object from CLLocationManager

CLLocation loc = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getLocationManager].location;

Then you can use your code feeding the location to reverseGeocodeLocation:completionHandler: and Get the CLPlaceMark

[geoCoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, 
NSError *error) {
    for (CLPlacemark * placemark in placemarks) {
        NSString *locality = [placemark locality];
        NSString * name =    [placemark name];
        NSString  *country  = [placemark country];
        /*you can put these values in some member vairables*/

        m_locality = [placemark locality];
        m_name =    [placemark name];
        m_country  = [placemark country];
    }
}
K'Prime
  • 98
  • 4
0

While I don't have a solution for the timezone issue (I agree with others who've answered this question - look for a non-Apple API), I thought I'd provide an answer in Swift, for those who are curious:

func provideGeocodedStringForLocation(location: CLLocation, withCompletionHandler completionHandler: (String) -> ()) {
  let geocoder = CLGeocoder()
  geocoder.reverseGeocodeLocation(location) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
    guard let placemarks = placemarks where placemarks.count > 0 && error == nil else {
      if let error = error { print(error.localizedDescription) }
      completionHandler("Earth")
      return
    }

    let city = placemarks[0].locality ?? ""
    let state: String
    if let adminArea = placemarks[0].administrativeArea {
      state = ", \(adminArea)"
    } else {
      state = ""
    }

    completionHandler("\(city)\(state)") // produces output similar to "Boulder, CO"
  }
}

// get the lat/long from your UIAppDelegate subclass
let latitude = CLLocationDegrees("40.0176")
let longitude = CLLocationDegrees("-105.28212")
let location = CLLocation(latitude: latitude, longitude: longitude)

provideGeocodedStringForLocation(location) { print($0) }
worthbak
  • 335
  • 3
  • 8
-1

I got timezone name from placemark description. And it works for ios 8 and higher.

You can check this link for how to get time zone

Community
  • 1
  • 1
Dharmesh Vaghani
  • 718
  • 6
  • 23