So I am trying to GeoCode an address and when someone types in "asdfsfdsf" it throws an error
"kCLErrorGeocodeFoundNoResult"
How can I catch the error so it doesn't show an ugly popup (i.e. with the error above) to the user?
-(void)geocodePinAddress:(NSString *)address withBlock:(void (^)(CLLocationCoordinate2D coord))block {
CLGeocoder* gc = [[CLGeocoder alloc] init];
__block CLLocationCoordinate2D coord;
[gc geocodeAddressString:address completionHandler: ^(NSArray *placemarks, NSError *error) {
// Check for returned placemarks
if (placemarks && placemarks.count > 0) {
CLPlacemark* mark = [placemarks objectAtIndex:0];
coord = mark.location.coordinate;
block(coord);
}
}];
}
Here is how you can handle geocoder domain errors :
if(placemarks.count > 0)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
self.outputLabel.text = placemark.location.description;
}
else if (error.domain == kCLErrorDomain)
{
switch (error.code)
{
case kCLErrorDenied:
self.outputLabel.text = #"Location Services Denied by User";
break;
case kCLErrorNetwork:
self.outputLabel.text = #"No Network";
break;
case kCLErrorGeocodeFoundNoResult:
self.outputLabel.text = #"No Result Found";
break;
default:
self.outputLabel.text = error.localizedDescription;
break;
}
}
Why not just show the message if an error occurs?
- (void)geocodePinAddress:(NSString *)address withBlock:(void (^)(CLLocationCoordinate2D coord))block {
CLGeocoder *gc = [[CLGeocoder alloc] init];
__block CLLocationCoordinate2D coord;
[gc geocodeAddressString:address completionHandler: ^(NSArray *placemarks, NSError *error) {
// if there was some error geocoding
if (error) {
// display whatever message you want, however you want, here
return;
}
// Check for returned placemarks
if (placemarks && placemarks.count > 0) {
CLPlacemark* mark = [placemarks objectAtIndex:0];
coord = mark.location.coordinate;
block(coord);
}
}];
}
Related
I want to find out photos location using reverseGeocodeLocation. I use the below function
#interface GalleryPhotosViewController ()
{
CLPlacemark *placemark;
}
#end
-(NSString *)getLocation:(CLLocation *)locations{
CLGeocoder *geocoder = [CLGeocoder new];
[geocoder reverseGeocodeLocation:locations completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
placemark = [placemarks lastObject];
}];
return placemark.name;
}
I could not get that name when call the function but I get that after execution of other parts of code. I know that reverseGeocodeLocation has a completion block, it is handed off to another thread when execution reaches it. But I need to get that name when call the function. I go through many solutions but could not solve my problem.
I want to receive location name in currentLocation.
CLLocation *loc = asset.location;
NSString *currentLocation = [self getLocation:loc];
Where should I change my code. Please help.
This is because you are returning value before you get it in completion handler of reverseGeocodeLocation. Now, you can't return the value from any completion block so what you need to do is create your own completionblock and call it when you get your result something like,
-(void)getLocation:(CLLocation *)locations withcompletionHandler : (void(^)(NSString *name))completionHandler{
CLGeocoder *geocoder = [CLGeocoder new];
[geocoder reverseGeocodeLocation:locations completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
placemark = [placemarks lastObject];
completionHandler(placemark.name);
}];
}
and call it like,
[self getLocation:location withcompletionHandler:^(NSString *name) {
NSLog(#"your name : %#",name); // or do whatever you want with name!
}];
Update :
If you want whole array in completion handler as you have mentioned in comment then your method should be like,
-(void)getLocation:(CLLocation *)locations withcompletionHandler : (void(^)(NSArray *arr))completionHandler{
CLGeocoder *geocoder = [CLGeocoder new];
[geocoder reverseGeocodeLocation:locations completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks,
NSError * _Nullable error) {
// placemark = [placemarks lastObject];
completionHandler(placemarks);
}];
}
and you can call it like,
[self getLocation:location withcompletionHandler:^(NSArray *arr) {
// you will get whole array here in `arr`, now you can manipulate it according to requirement
NSLog(#"your response array : %#",arr);
CLPlacemark *placemark = [arr lastObject];
}];
The method reverseGeocodeLocation of class CLGeocoder runs asynchronously hence you will not get the location data as a return value from the called function. It uses the completion handler where the location data is returned and you can use that completion handler block to update the UI or any further processing of your location data.
Get city,state and address you have to pass latitude and longitude,
so pass latitude and longitude will pass to geocoder block and
within block you will placemarks array.
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *myLocation = [[CLLocation alloc]initWithLatitude:23.0225
longitude:72.5714];
[geocoder reverseGeocodeLocation:myLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"Geocode failed with error: %#", error);
return;
}
if (placemarks && placemarks.count > 0)
{
CLPlacemark *placemark = placemarks[0];
NSDictionary *addressDictionary =
placemark.addressDictionary;
NSLog(#"%# ", addressDictionary);
NSString *address = [addressDictionary
objectForKey:(NSString *)kABPersonAddressStreetKey];
NSString *city = [addressDictionary
objectForKey:(NSString *)kABPersonAddressCityKey];
NSString *state = [addressDictionary
objectForKey:(NSString *)kABPersonAddressStateKey];
NSLog(#"%# %# %# %#", address,city, state);
}
}];
I am using reverse geocoding which works but I am trying to separate the attributes. For example I want to have zip code displayed in it's own text field.
This is what I've tried but I get a breakpoint at if([placemarks count] >0)
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// Make sure this is a recent location event
CLLocation *newLocation = [locations lastObject];
NSTimeInterval eventInterval = [newLocation.timestamp timeIntervalSinceNow];
if(abs(eventInterval) < 30.0)
{
// Make sure the event is valid
if (newLocation.horizontalAccuracy < 0)
return;
// Instantiate _geoCoder if it has not been already
if (_geocoder == nil)
_geocoder = [[CLGeocoder alloc] init];
//Only one geocoding instance per action
//so stop any previous geocoding actions before starting this one
if([_geocoder isGeocoding])
[_geocoder cancelGeocode];
[_geocoder reverseGeocodeLocation: newLocation
completionHandler: ^(NSArray* placemarks, NSError* error)
{
if([placemarks count] > 0)
{
CLPlacemark *foundPlacemark = [placemarks objectAtIndex:0];
self.address.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.description];
self.zip.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.postalCode];
}
else if (error.code == kCLErrorGeocodeCanceled)
{
NSLog(#"Geocoding cancelled");
}
else if (error.code == kCLErrorGeocodeFoundNoResult)
{
self.address.text=#"No geocode result found";
}
else if (error.code == kCLErrorGeocodeFoundPartialResult)
{
self.address.text=#"Partial geocode result";
}
else
{
self.address.text =
[NSString stringWithFormat:#"Unknown error: %#",
error.description];
}
}
];
Try to handle error first by checking if its nil:
if (error) {
// handle here
NSLog(#"error: %#", error);
return; // exit statement
}
// handle code here if there was no error
and avoid strong reference cycles create a weak object of your self, when calling a object use this instnace.
__weak typeof(self) weakSelf = self;
so self.address.text=#"Partial geocode result"; will turn to weakSelf.address.text=#"some text here...";
I have an app that fills in the user location into a textfield, if the user allows location sharing. Everything works but my question is how do I get the results to be split up into multiple text fields.
Currently the "address" text field displays everything. All I want in that field is the street address, and another one for City, State, Zip, etc. Any suggestions would be appreciated! I've looked at the documentation for example for zip code I've tried
self.zip.text=
NSString *postalCode = [NSString stringWithFormat:#"%#",placemark.postalCode];
but that doesn't work
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if(error.code == kCLErrorDenied)
{
self.address.text = #"Location information denied";
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// Make sure this is a recent location event
CLLocation *newLocation = [locations lastObject];
NSTimeInterval eventInterval = [newLocation.timestamp timeIntervalSinceNow];
if(abs(eventInterval) < 30.0)
{
// Make sure the event is valid
if (newLocation.horizontalAccuracy < 0)
return;
// Instantiate _geoCoder if it has not been already
if (_geocoder == nil)
_geocoder = [[CLGeocoder alloc] init];
//Only one geocoding instance per action
//so stop any previous geocoding actions before starting this one
if([_geocoder isGeocoding])
[_geocoder cancelGeocode];
[_geocoder reverseGeocodeLocation: newLocation
completionHandler: ^(NSArray* placemarks, NSError* error)
{
if([placemarks count] > 0)
{
CLPlacemark *foundPlacemark = [placemarks objectAtIndex:0];
self.address.text =
[NSString stringWithFormat:#"%#",
foundPlacemark.description];
}
else if (error.code == kCLErrorGeocodeCanceled)
{
NSLog(#"Geocoding cancelled");
}
else if (error.code == kCLErrorGeocodeFoundNoResult)
{
self.address.text=#"No geocode result found";
}
else if (error.code == kCLErrorGeocodeFoundPartialResult)
{
self.address.text=#"Partial geocode result";
}
else
{
self.address.text =
[NSString stringWithFormat:#"Unknown error: %#",
error.description];
}
}
];
//Stop updating location until they click the button again
[manager stopUpdatingLocation];
}
}
You should check the reference page for CLPlacemark class.
There you will find the properties you need: postal code, locality ...
CLPlacemark *foundPlacemark = [placemarks lastObject];
NSString *subThoroughfare = [NSString stringWithFormat:#"%#",placemark.subThoroughfare];
NSString *thoroughfare = [NSString stringWithFormat:#"%#",placemark.thoroughfare];
NSString *postalCode = [NSString stringWithFormat:#"%#",placemark.postalCode];
NSString *country = [NSString stringWithFormat:#"%#",placemark.country];
NSString *locality = [NSString stringWithFormat:#"%#",placemark.locality];
NSString *administrativeArea = [NSString stringWithFormat:#"%#",placemark.administrativeArea];
I got the current location based on the Longitude and Latitude values and then I also got multiple places on google map using Annotation. Now I want to get the longitude and latitude values based on the Address( i.e street,city and county).Need some guidance on how this can be achieved.
Till now, this is what I have tried:-
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize streetField = _streetField, cityField = _cityField, countryField = _countryField, fetchCoordinatesButton = _fetchCoordinatesButton, nameLabel = _nameLabel, coordinatesLabel = _coordinatesLabel;
#synthesize geocoder = _geocoder;
- (void)viewDidLoad
{
[super viewDidLoad];
_streetField.delegate=self;
_cityField.delegate=self;
_countryField.delegate=self;
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)fetchCoordinates:(id)sender {
NSLog(#"Fetch Coordinates");
if (!self.geocoder) {
NSLog(#"Geocdoing");
self.geocoder = [[CLGeocoder alloc] init];
}
NSString *address = [NSString stringWithFormat:#"%# %# %#", self.streetField.text, self.cityField.text, self.countryField.text];
NSLog(#"GET Addres%#",address);
self.fetchCoordinatesButton.enabled = NO;
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Fetch Gecodingaddress");
if ([placemarks count] > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"GET placemark%#",placemark);
CLLocation *location = placemark.location;
NSLog(#"GET location%#",location);
CLLocationCoordinate2D coordinate = location.coordinate;
self.coordinatesLabel.text = [NSString stringWithFormat:#"%f, %f", coordinate.latitude, coordinate.longitude];
NSLog(#"CoordinatesLabel%#",self.coordinatesLabel.text);
if ([placemark.areasOfInterest count] > 0) {
NSString *areaOfInterest = [placemark.areasOfInterest objectAtIndex:0];
self.nameLabel.text = areaOfInterest;
NSLog(#"NameLabe%#",self.nameLabel.text);
}
}
self.fetchCoordinatesButton.enabled = YES;
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
#end
Above code is not working to give me the latitude and longitude.Need some help on what am I doing wrong here or if I am missing on something.
Thanks in Advance.
This was very old answer, Kindly check with new Updates
EDIT:
Before using this check with iOS8 updation
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
This is for getting lat and long based user area like street name,state name,country.
-(CLLocationCoordinate2D) getLocationFromAddressString: (NSString*) addressStr {
double latitude = 0, longitude = 0;
NSString *esc_addr = [addressStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *req = [NSString stringWithFormat:#"http://maps.google.com/maps/api/geocode/json?sensor=false&address=%#", esc_addr];
NSString *result = [NSString stringWithContentsOfURL:[NSURL URLWithString:req] encoding:NSUTF8StringEncoding error:NULL];
if (result) {
NSScanner *scanner = [NSScanner scannerWithString:result];
if ([scanner scanUpToString:#"\"lat\" :" intoString:nil] && [scanner scanString:#"\"lat\" :" intoString:nil]) {
[scanner scanDouble:&latitude];
if ([scanner scanUpToString:#"\"lng\" :" intoString:nil] && [scanner scanString:#"\"lng\" :" intoString:nil]) {
[scanner scanDouble:&longitude];
}
}
}
CLLocationCoordinate2D center;
center.latitude=latitude;
center.longitude = longitude;
NSLog(#"View Controller get Location Logitute : %f",center.latitude);
NSLog(#"View Controller get Location Latitute : %f",center.longitude);
return center;
}
call the method like this in viewdidload method or somewhere according to your project
[self getLocationFromAddressString:#"chennai"];
just pass this in your browser
http://maps.google.com/maps/api/geocode/json?sensor=false&address=chennai
and you will have json format with lat and lon
http://maps.google.com/maps/api/geocode/json?sensor=false&address=#"your city name here"
NSString *address = [NSString stringWithFormat:#"http://maps.google.com/maps/api/geocode/json?sensor=false&address=%# %# %#", self.streetField.text, self.cityField.text, self.countryField.text];
the usage of this method....
CLLocationCoordinate2D center;
center=[self getLocationFromAddressString:#"uthangarai"];
double latFrom=¢er.latitude;
double lonFrom=¢er.longitude;
NSLog(#"View Controller get Location Logitute : %f",latFrom);
NSLog(#"View Controller get Location Latitute : %f",lonFrom);
Someone looking for Swift 2.0 solution can use below :
let address = "1 Infinite Loop, CA, USA"
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error)
}
if let placemark = placemarks?.first {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
coordinates.latitude
coordinates.longitude
print("lat", coordinates.latitude)
print("long", coordinates.longitude)
}
})
Try this,
NSString *address = [NSString stringWithFormat:#"%#,%#,%#", self.streetField.text, self.cityField.text,self.countryField.text];
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error)
{
if(!error)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSLog(#"%f",placemark.location.coordinate.latitude);
NSLog(#"%f",placemark.location.coordinate.longitude);
NSLog(#"%#",[NSString stringWithFormat:#"%#",[placemark description]]);
}
else
{
NSLog(#"There was a forward geocoding error\n%#",[error localizedDescription]);
}
}
];
Swift
public func getLocationFromAddress(address : String) -> CLLocationCoordinate2D {
var lat : Double = 0.0
var lon : Double = 0.0
do {
let url = String(format: "https://maps.google.com/maps/api/geocode/json?sensor=false&address=%#", (address.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!))
let result = try Data(contentsOf: URL(string: url)!)
let json = JSON(data: result)
lat = json["results"][0]["geometry"]["location"]["lat"].doubleValue
lon = json["results"][0]["geometry"]["location"]["lng"].doubleValue
}
catch let error{
print(error)
}
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
I used SwiftyJSON but you can parse the JSON response however you want
- (IBAction)forwardButton:(id)sender
{
if([self.address.text length])
{
NSString *place = self.address.text;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
__unsafe_unretained RTGeoCoderViewController *weakSelf = self;
[geocoder geocodeAddressString:place completionHandler:^(NSArray* placemarks, NSError* error)
{
NSLog(#"completed");
if ( error )
{
NSLog(#"error = %#", error );
dispatch_async(dispatch_get_main_queue(),
^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[self errorMessage:error.code] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
});
}
else
{
NSLog(#"%#",placemarks);
}
}];
}
}
Try this
- (void)getAddressFromAdrress:(NSString *)address withCompletationHandle:(void (^)(NSDictionary *))completationHandler {
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
//Get the address through geoCoder
[geoCoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0 && !error) {
//get the address from placemark
CLPlacemark *placemark = [placemarks objectAtIndex:0];
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
CLLocation *location = placemark.location;
CLLocationCoordinate2D coordinate = location.coordinate;
_latitudeUserLocation = coordinate.latitude;
_longitudeUserLocation = coordinate.longitude;
NSString *postalCode = placemark.addressDictionary[(NSString*)kABPersonAddressZIPKey];
if (postalCode == nil) postalCode = #"";
if (locatedAt == nil) locatedAt = #"";
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
postalCode ,kPostalCode,
locatedAt ,kFullAddress,
nil];
completationHandler(dictionary);
} else {
completationHandler(nil);
}
}];
}
I am very new to xcode please guide me to correct the error, My code is,
//Set our mapView
[MapViewC setRegion:myRegion animated:NO];
CLLocation *someLocation=[[CLLocation alloc]initWithLatitude:latitude longitude:longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:someLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
addressOutlet=[dictionary valueForKey:#"Street"];
City=[dictionary valueForKey:#"City"];
State=[dictionary valueForKey:#"State"];
if (addressOutlet!=NULL&&City!=NULL)
{
NSString *SubTitle=[NSString stringWithFormat:#"%#,%#,%#",addressOutlet,City,State];
cell.detailTextLabel.text=SubTitle;
}
else if (addressOutlet==NULL&&City!=NULL)
{
NSString *SubTitle=[NSString stringWithFormat:#"%#,%#,",City,State];
cell.detailTextLabel.text=SubTitle;
}
else if (addressOutlet!=NULL&&City==NULL)
{
NSString *SubTitle=[NSString stringWithFormat:#"%#,%#,",addressOutlet,State];
cell.detailTextLabel.text=SubTitle;
}
else if(addressOutlet==NULL&&City==NULL)
{
NSString *SubTitle=[NSString stringWithFormat:#"%#",State];
cell.detailTextLabel.text=SubTitle;
}
}];
Thanks in advance.
OK an array can be empty, in which case the following statement will cause an exception:
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
// ^
So guard against it, with something like:
if ([placemarks count] > 0) {
NSDictionary *dictionary = [[placemarks objectAtIndex:0] addressDictionary];
....
}
I haven't read the API docs for that completion handler, but you can probably test if error is non-nil and act accordingly (i.e. if error != nil respond to error, else process the placemarks array).