1

I am new to ObjC and I am struggling with the CLGeocoder. I want to be able to use reverseGeocodeLocation to obtain a string that contains the user location that I pass to my delegate when the user presses a Done button.

So the user triggers the display of a MapViewController, I call the reverseGeocodeLocation in the viewDidLoad but the [placemarks count = 0] this first time in, and I have no placemark to get the info that I need. The second time the user triggers the display of the MapViewController the placemarks array has been populated and everything works.

I suspect it is something to do with the reverseGeocodeLocation being an asynchronous call - but I cannot figure out how to solve this problem. I have tried searching online but nothing seems to help me understand what I am doing wrong and how i can solve this issue. Thanks in advance.

@interface MapViewController ()
@property (strong, nonatomic) CLGeocoder *geocoder;
@property (readwrite, nonatomic) NSString *theLocationName;
@end

@implementation MapViewController
@synthesize mapView, geocoder, delegate = _delegate, theLocationName = _theLocationName;

- (void)viewDidLoad
{
[super viewDidLoad];

self.mapView.delegate=self;
self.mapView.showsUserLocation = YES;

[self theUserLocation];
}

-(void)theUserLocation
{
if (!geocoder)
{
    geocoder = [[CLGeocoder alloc] init];
}

MKUserLocation *theLocation;
theLocation = [self.mapView userLocation];

[geocoder reverseGeocodeLocation:theLocation.location 
               completionHandler:^(NSArray* placemarks, NSError* error)
 {
     if ([placemarks count] > 0)
     {
         CLPlacemark *placemark = [placemarks objectAtIndex:0];

         [self setTheLocationName: placemark.locality];

     }
 }];

- (IBAction)done:(id)sender 
{

[[self delegate] mapViewControllerDidFinish:self locationName:[self theLocationName]];

}

@end
Byron Cox
  • 349
  • 5
  • 17

2 Answers2

3

This is not exact answer to your question but, if you can switch to other solution apart from CLGeocoder than following function can help you to get address from given latitude, longitude

#define kGeoCodingString @"http://maps.google.com/maps/geo?q=%f,%f&output=csv" //define this at top

-(NSString *)getAddressFromLatLon:(double)pdblLatitude withLongitude:(double)pdblLongitude
{
    NSString *urlString = [NSString stringWithFormat:kGeoCodingString,pdblLatitude, pdblLongitude];
    NSError* error;
    NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSASCIIStringEncoding error:&error];
    locationString = [locationString stringByReplacingOccurrencesOfString:@"\"" withString:@""];
    return [locationString substringFromIndex:6];
}

Credit : Selected Answer to this question

Community
  • 1
  • 1
Janak Nirmal
  • 22,706
  • 18
  • 63
  • 99
  • Thank you for your reply. I will keep this in mind but I really want to understand how the CLGeocoder works :-) – Byron Cox Jul 02 '12 at 23:15
2

So the user triggers the display of a MapViewController, I call the reverseGeocodeLocation in the viewDidLoad but the [placemarks count = 0] this first time in, and I have no placemark to get the info that I need. The second time the user triggers the display of the MapViewController the placemarks array has been populated and everything works.

It's not because the call is asynchronous - it's because the first time you call theUserLocation the actual location isn't available. Getting the user's location is not instantaneous - it takes time. However, you're asking for the user's location as soon as the map loads, which in most circumstances won't work.

What you need to do is hook into the MKMapViewDelegate methods, which provide you with callbacks when the location is updated. You can use this to check the location's accuracy, and decide whether it is accurate enough for you to reverse geolocate.

lxt
  • 31,146
  • 5
  • 78
  • 83
  • Thank you for your reply. Accept my apologies as I am still desperately trying to understand how this works. Given that I hadn't left enough time for the user's location to be known, I thought that i would shift the call to [self theUserLocation] to the implementation of the Done button. My reasoning being that by this time the map has already been displayed and is showing a pin with the zoomed in location of the user - so the user location must be known by then - but this doesn't work either. – Byron Cox Jul 02 '12 at 22:34
  • 1
    Ah, just dawned on me, because the geocoder is taking time to do a lat/long lookup, so the rest of the Done code is executing before the geocoder can supply its answer. – Byron Cox Jul 02 '12 at 22:50
  • I had already implemented the didUpdateUserLocation: method where I had put all the code for zooming in on the user's location. I just moved the [self theUserLocation] method into here - and now it works!!! I am not 100% sure this is watertight, as I am worried that if the geocoder took a while to do its lat/long lookup that the user could still hit the Done button before it had an answer? – Byron Cox Jul 02 '12 at 23:12