如何检索用户的当前城市名称?

如何检索用户的当前城市名称?

67902 次浏览

Read the documentation for MKReverseGeocoder -- the documentation, guides & sample applications are provided by Apple for a reason.

You have to get user's current location and then use MKReverseGeocoder to recognize the city.

There's great example in iPhone App Programming Guide, chapter 8. Once you've got location initialize geocoder, set the delegate and read country from the placemark. Read the documentation for MKReverseGeocodeDelegate and create methods:

  • reverseGeocoder:didFindPlacemark:
  • reverseGeocoder:didFailWithError:

    MKReverseGeocoder *geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];
    geocoder.delegate = self;
    [geocoder start];
    

What you have to do is setup a CLLocationManager that will find your current coordinates. With the current coordinates you need to use MKReverseGeoCoder to find your location.

- (void)viewDidLoad
{
// this creates the CCLocationManager that will find your current location
CLLocationManager *locationManager = [[[CLLocationManager alloc] init] autorelease];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[locationManager startUpdatingLocation];
}


// this delegate is called when the app successfully finds your current location
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// this creates a MKReverseGeocoder to find a placemark using the found coordinates
MKReverseGeocoder *geoCoder = [[MKReverseGeocoder alloc] initWithCoordinate:newLocation.coordinate];
geoCoder.delegate = self;
[geoCoder start];
}


// this delegate method is called if an error occurs in locating your current location
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"locationManager:%@ didFailWithError:%@", manager, error);
}


// this delegate is called when the reverseGeocoder finds a placemark
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
{
MKPlacemark * myPlacemark = placemark;
// with the placemark you can now retrieve the city name
NSString *city = [myPlacemark.addressDictionary objectForKey:(NSString*) kABPersonAddressCityKey];
}


// this delegate is called when the reversegeocoder fails to find a placemark
- (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
{
NSLog(@"reverseGeocoder:%@ didFailWithError:%@", geocoder, error);
}

As of iOS 5 MKReverseGeoCoder is Deprecated!

So you want to use CLGeocoder with CLLocationManager, very simple and works with block.

Example:

- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
[self.locationManager stopUpdatingLocation];


CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:newLocation
completionHandler:^(NSArray *placemarks, NSError *error) {
for (CLPlacemark *placemark in placemarks) {
.... = [placemark locality];
}
}];
}

Edit: Instead of a for in loop you can also do:

NSString *locString = placemarks.count ? [placemarks.firstObject locality] : @"Not Found";

If anyone is trying to move onto CLGeocoder from MKReverseGeocoder then I have written a blog post that might be of help http://jonathanfield.me/jons-blog/clgeocoder-example.html

Basically an example would be, after you have created locationManager and CLGeocoder objects just add this code to your viewDidLoad() and then make some labels or text areas to show the data.

  [super viewDidLoad];
locationManager.delegate = self;
[locationManager startUpdatingLocation];


locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[self.CLGeocoder reverseGeocodeLocation: locationManager.location completionHandler:
^(NSArray *placemarks, NSError *error) {




CLPlacemark *placemark = [placemarks objectAtIndex:0];




isoCountryCode.text = placemark.ISOcountryCode;
country.text = placemark.country;
postalCode.text= placemark.postalCode;
adminArea.text=placemark.administrativeArea;
subAdminArea.text=placemark.subAdministrativeArea;
locality.text=placemark.locality;
subLocality.text=placemark.subLocality;
thoroughfare.text=placemark.thoroughfare;
subThoroughfare.text=placemark.subThoroughfare;
//region.text=placemark.region;






}];

This is working fine for me :

CLGeocoder *geocoder = [[CLGeocoder alloc] init] ;
[geocoder reverseGeocodeLocation:self.locationManager.location
completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(@"reverseGeocodeLocation:completionHandler: Completion Handler called!");


if (error){
NSLog(@"Geocode failed with error: %@", error);
return;


}




CLPlacemark *placemark = [placemarks objectAtIndex:0];


NSLog(@"placemark.ISOcountryCode %@",placemark.ISOcountryCode);
NSLog(@"placemark.country %@",placemark.country);
NSLog(@"placemark.postalCode %@",placemark.postalCode);
NSLog(@"placemark.administrativeArea %@",placemark.administrativeArea);
NSLog(@"placemark.locality %@",placemark.locality);
NSLog(@"placemark.subLocality %@",placemark.subLocality);
NSLog(@"placemark.subThoroughfare %@",placemark.subThoroughfare);


}];

You can use this code for getting Current City:--

extension YourController: CLLocationManagerDelegate { func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in


if (error != nil)
{
manager.stopUpdatingLocation()
return
}
else
{
if placemarks!.count > 0
{
let placeMarksArrray: NSArray = placemarks!
let pm = placeMarksArrray[0] as! CLPlacemark
self.displayLocationInfo(pm)
manager.stopUpdatingLocation()
} else
{
print("Problem with the data received from geocoder")
}
}
})
}


func displayLocationInfo(placemark: CLPlacemark!) {
if (placemark != nil) {
//stop updating location to save battery life
locationLocation.stopUpdatingLocation()
var tempString : String = ""
if(placemark.locality != nil){
tempString = tempString +  placemark.locality! + " "
print(placemark.locality)
}
if(placemark.postalCode != nil){
tempString = tempString +  placemark.postalCode! + " "
print(placemark.postalCode)
}
if(placemark.administrativeArea != nil){
tempString = tempString +  placemark.administrativeArea! + " "
print(placemark.administrativeArea)
}
if(placemark.country != nil){
tempString = tempString +  placemark.country! + " "






}
let dictForaddress = placemark.addressDictionary as! NSDictionary




if let city = dictForaddress["City"] {
print(city)






}
strLocation = tempString
}
}
// place the function code below in desire location in program.


// [self getCurrentLocation];






-(void)getCurrentLocation
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init] ;
[geocoder reverseGeocodeLocation:self->locationManager.location
completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(@"reverseGeocodeLocation:completionHandler: Completion Handler called!");


if (error){
NSLog(@"Geocode failed with error: %@", error);
return;


}




CLPlacemark *placemark = [placemarks objectAtIndex:0];


NSLog(@"placemark.ISOcountryCode %@",placemark.ISOcountryCode);
NSLog(@"placemark.country %@",placemark.country);
NSLog(@"placemark.locality %@",placemark.locality );
NSLog(@"placemark.postalCode %@",placemark.postalCode);
NSLog(@"placemark.administrativeArea %@",placemark.administrativeArea);
NSLog(@"placemark.locality %@",placemark.locality);
NSLog(@"placemark.subLocality %@",placemark.subLocality);
NSLog(@"placemark.subThoroughfare %@",placemark.subThoroughfare);


}];
}

If anyone needs it in Swift 3, this is how I did it:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {


let location = locations.first!
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 500, 500)


self.location = location
self.locationManager?.stopUpdatingLocation()


// Drop a pin at user's Current Location
let myAnnotation: MKPointAnnotation = CustomPointAnnotation()
myAnnotation.coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
myAnnotation.title = "Localização"
self.mapViewMK.addAnnotation(myAnnotation)


self.mapViewMK.setRegion(coordinateRegion, animated: true)
self.locationManager?.stopUpdatingLocation()
self.locationManager = nil


// Get user's current location name
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(self.location!) { (placemarksArray, error) in


if (placemarksArray?.count)! > 0 {


let placemark = placemarksArray?.first
let number = placemark!.subThoroughfare
let bairro = placemark!.subLocality
let street = placemark!.thoroughfare


self.addressLabel.text = "\(street!), \(number!) - \(bairro!)"
}
}
}

After you setup CLLocationManager you would get location update as a latitude/longitude pair. Then you could use CLGeocoder to convert the coordinate to a user friendly place name.

Here is the sample code in Swift 4.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geocoder = CLGeocoder()


geocoder.reverseGeocodeLocation(lastLocation) { [weak self] (placemarks, error) in
if error == nil {
if let firstLocation = placemarks?[0],
let cityName = firstLocation.locality { // get the city name
self?.locationManager.stopUpdatingLocation()
}
}
}
}
}

Here is my small Swift class which helps me with getting reverse geocoded information about current location. Don't forget about NSLocationWhenInUseUsageDescription field in Info.plist.

class CurrentPlacemarkUpdater: NSObject, CLLocationManagerDelegate {


private let locationManager = CLLocationManager()
private let geocoder = CLGeocoder()


private(set) var latestPlacemark: CLPlacemark?
var onLatestPlacemarkUpdate: (() -> ())?
var shouldStopOnUpdate: Bool = true


func start() {
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
locationManager.delegate = self
locationManager.startUpdatingLocation()
}


func stop() {
locationManager.stopUpdatingLocation()
}


fileprivate func updatePlacemark(for location: CLLocation) {
geocoder.reverseGeocodeLocation(location) { [weak self] placemarks, error in
if let placemark = placemarks?.first {
self?.latestPlacemark = placemark
self?.onLatestPlacemarkUpdate?()
if self?.shouldStopOnUpdate ?? false {
self?.stop()
}
}
}
}


func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
updatePlacemark(for: location)
}
}


func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("CurrentPlacemarkUpdater: \(error)")
}


}