50

如何使用 MapKit 从坐标中获取地址?

长按地图时我有此代码,它会获取坐标:

func didLongPressMap(sender: UILongPressGestureRecognizer) {

    if sender.state == UIGestureRecognizerState.Began {
        let touchPoint = sender.locationInView(self.mapView)
        let touchCoordinate = self.mapView.convertPoint(touchPoint, toCoordinateFromView: self.mapView)
        var annotation = MKPointAnnotation()
        annotation.coordinate = touchCoordinate
        annotation.title = "Your position"
        self.mapView.addAnnotation(annotation) //drops the pin
        println("lat:  \(touchCoordinate.latitude)")
        var num = (touchCoordinate.latitude as NSNumber).floatValue
        var formatter = NSNumberFormatter()
        formatter.maximumFractionDigits = 4
        formatter.minimumFractionDigits = 4
        var str = formatter.stringFromNumber(num)
        println("long: \(touchCoordinate.longitude)")
        var num1 = (touchCoordinate.longitude as NSNumber).floatValue
        var formatter1 = NSNumberFormatter()
        formatter1.maximumFractionDigits = 4
        formatter1.minimumFractionDigits = 4
        var str1 = formatter1.stringFromNumber(num1)
        self.adressLoLa.text = "\(num),\(num1)"
                }
}

我想打印annotation.title完整的地址(街道、城市、邮编、国家)。

4

9 回答 9

82

斯威夫特 4.2:编辑


MapKit框架确实提供了一种从坐标中获取地址详细信息的方法。

您需要使用地图套件的反向地理编码CLGeocoder类用于从地址获取位置,从位置(坐标)获取地址。该方法reverseGeocodeLocation将从坐标返回地址详细信息。

此方法接受CLLocation作为参数并返回CLPlacemark,其中包含地址字典。

所以现在上面的方法将被更新为:

@objc func didLongPressMap(sender: UILongPressGestureRecognizer) {

    if sender.state == UIGestureRecognizer.State.began {
        let touchPoint = sender.location(in: mapView)
        let touchCoordinate = mapView.convert(touchPoint, toCoordinateFrom: self.mapView)
        let annotation = MKPointAnnotation()
        annotation.coordinate = touchCoordinate
        annotation.title = "Your position"
        mapView.addAnnotation(annotation) //drops the pin
        print("lat:  \(touchCoordinate.latitude)")
        let num = touchCoordinate.latitude as NSNumber
        let formatter = NumberFormatter()
        formatter.maximumFractionDigits = 4
        formatter.minimumFractionDigits = 4
        _ = formatter.string(from: num)
        print("long: \(touchCoordinate.longitude)")
        let num1 = touchCoordinate.longitude as NSNumber
        let formatter1 = NumberFormatter()
        formatter1.maximumFractionDigits = 4
        formatter1.minimumFractionDigits = 4
        _ = formatter1.string(from: num1)
        self.adressLoLa.text = "\(num),\(num1)"

        // Add below code to get address for touch coordinates.
        let geoCoder = CLGeocoder()
        let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)
        geoCoder.reverseGeocodeLocation(location, completionHandler:
            {
                placemarks, error -> Void in

                // Place details
                guard let placeMark = placemarks?.first else { return }

                // Location name
                if let locationName = placeMark.location {
                    print(locationName)
                }
                // Street address
                if let street = placeMark.thoroughfare {
                    print(street)
                }
                // City
                if let city = placeMark.subAdministrativeArea {
                    print(city)
                }
                // Zip code
                if let zip = placeMark.isoCountryCode {
                    print(zip)
                }
                // Country
                if let country = placeMark.country {
                    print(country)
                }
        })
    }
}
于 2015-01-02T10:29:20.727 回答
37

对于Swift 3:Swift 4

首先,您需要在 中设置接收用户 GPS 的限额info.plist

在此处输入图像描述

设置:NSLocationWhenInUseUsageDescription带有随机字符串。和/或:NSLocationAlwaysUsageDescription使用随机字符串。

然后我设置了一个类来获取所需的数据,例如 zip、城镇、国家...:

import Foundation
import MapKit

typealias JSONDictionary = [String:Any]

class LocationServices {

    let shared = LocationServices()
    let locManager = CLLocationManager()
    var currentLocation: CLLocation!

    let authStatus = CLLocationManager.authorizationStatus()
    let inUse = CLAuthorizationStatus.authorizedWhenInUse
    let always = CLAuthorizationStatus.authorizedAlways

    func getAdress(completion: @escaping (_ address: JSONDictionary?, _ error: Error?) -> ()) {

        self.locManager.requestWhenInUseAuthorization()

        if self.authStatus == inUse || self.authStatus == always {

            self.currentLocation = locManager.location

            let geoCoder = CLGeocoder()

            geoCoder.reverseGeocodeLocation(self.currentLocation) { placemarks, error in

                if let e = error {

                    completion(nil, e)

                } else {

                    let placeArray = placemarks as? [CLPlacemark]

                    var placeMark: CLPlacemark!

                    placeMark = placeArray?[0]

                    guard let address = placeMark.addressDictionary as? JSONDictionary else {
                        return
                    }

                    completion(address, nil)

                }

            }

        }

    }

}

调用者:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        LocationServices.shared.getAdress { address, error in

            if let a = address, let city = a["City"] as? String {
               //
            }

        }

    }

}

完毕

于 2016-11-09T20:51:24.653 回答
30
import Foundation
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

let location = CLLocation(latitude: 37.3321, longitude: -122.0318)
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
    
    guard let placemark = placemarks?.first else {
        let errorString = error?.localizedDescription ?? "Unexpected Error"
        print("Unable to reverse geocode the given location. Error: \(errorString)")
        return
    }
    
    let reversedGeoLocation = ReversedGeoLocation(with: placemark)
    print(reversedGeoLocation.formattedAddress)
    // Apple Inc.,
    // 1 Infinite Loop,
    // Cupertino, CA 95014
    // United States
}

struct ReversedGeoLocation {
    let name: String            // eg. Apple Inc.
    let streetNumber: String    // eg. 1
    let streetName: String      // eg. Infinite Loop
    let city: String            // eg. Cupertino
    let state: String           // eg. CA
    let zipCode: String         // eg. 95014
    let country: String         // eg. United States
    let isoCountryCode: String  // eg. US
    
    var formattedAddress: String {
        return """
        \(name),
        \(streetNumber) \(streetName),
        \(city), \(state) \(zipCode)
        \(country)
        """
    }
    
    // Handle optionals as needed
    init(with placemark: CLPlacemark) {
        self.name           = placemark.name ?? ""
        self.streetName     = placemark.thoroughfare ?? ""
        self.streetNumber   = placemark.subThoroughfare ?? ""
        self.city           = placemark.locality ?? ""
        self.state          = placemark.administrativeArea ?? ""
        self.zipCode        = placemark.postalCode ?? ""
        self.country        = placemark.country ?? ""
        self.isoCountryCode = placemark.isoCountryCode ?? ""
    }
}

旧/不推荐使用的答案:

感谢@Kampai 的回答,这是一种兼容Swift 3且更安全的方式(无强制!):

let geoCoder = CLGeocoder()
let location = CLLocation(latitude: touchCoordinate.latitude, longitude: touchCoordinate.longitude)

geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
    guard let addressDict = placemarks?[0].addressDictionary else {
        return
    }
    
    // Print each key-value pair in a new row
    addressDict.forEach { print($0) }
    
    // Print fully formatted address
    if let formattedAddress = addressDict["FormattedAddressLines"] as? [String] {
        print(formattedAddress.joined(separator: ", "))
    }
    
    // Access each element manually
    if let locationName = addressDict["Name"] as? String {
        print(locationName)
    }
    if let street = addressDict["Thoroughfare"] as? String {
        print(street)
    }
    if let city = addressDict["City"] as? String {
        print(city)
    }
    if let zip = addressDict["ZIP"] as? String {
        print(zip)
    }
    if let country = addressDict["Country"] as? String {
        print(country)
    }
})

不要忘记NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription钥匙

于 2017-01-05T19:54:33.720 回答
22

感谢@Kampi。这是更新的 Swift 2.0 (Xcode 7) 版本:

func setUsersClosestCity()
{
    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: _point1.coordinate.latitude, longitude: _point1.coordinate.longitude)
    geoCoder.reverseGeocodeLocation(location)
    {
        (placemarks, error) -> Void in

        let placeArray = placemarks as [CLPlacemark]!

        // Place details
        var placeMark: CLPlacemark!
        placeMark = placeArray?[0]

        // Address dictionary
        print(placeMark.addressDictionary)

        // Location name
        if let locationName = placeMark.addressDictionary?["Name"] as? NSString
        {
            print(locationName)
        }

        // Street address
        if let street = placeMark.addressDictionary?["Thoroughfare"] as? NSString
        {
            print(street)
        }

        // City
        if let city = placeMark.addressDictionary?["City"] as? NSString
        {
            print(city)
        }

        // Zip code
        if let zip = placeMark.addressDictionary?["ZIP"] as? NSString
        {
            print(zip)
        }

        // Country
        if let country = placeMark.addressDictionary?["Country"] as? NSString
        {
            print(country)
        }
    }
}
于 2015-09-28T22:08:45.130 回答
10

感谢@Kampai 的回答,我做了一些修改,所以它适用于Swift 1.2

        var geocoder = CLGeocoder()
        var location = CLLocation(latitude: IC.coordinate!.latitude, longitude: IC.coordinate!.longitude)
        geocoder.reverseGeocodeLocation(location) {
            (placemarks, error) -> Void in
            if let placemarks = placemarks as? [CLPlacemark] where placemarks.count > 0 {
                var placemark = placemarks[0]
                println(placemark.addressDictionary)
        }

结果:

[
    SubLocality: Sydney, 
    Street: 141 Harrington Street, 
    State: NSW, 
    SubThoroughfare: 141, 
    CountryCode: AU, ZIP: 2000, 
    Thoroughfare: Harrington Street, 
    Name: 141 Harrington Street, 
    Country: Australia, FormattedAddressLines: (
        "141 Harrington Street",
        "The Rocks NSW 2000",
        Australia
    ), 
    City: The Rocks
]
于 2015-06-25T02:38:49.603 回答
6

Swift 4.2 保持尽可能简单,查看 Apple文档并根据需要进行修改:

func retreiveCityName(lattitude: Double, longitude: Double, completionHandler: @escaping (String?) -> Void)
{
    let geocoder = CLGeocoder()
    geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler:
    {
        placeMarks, error in

        completionHandler(placeMarks?.first?.locality)
     })
}
于 2018-11-20T19:19:30.230 回答
5

更新斯威夫特 4

addressDictionary在 iOS 11.0中已弃用

let geoCoder = CLGeocoder()
let location = CLLocation(latitude: 37.769193, longitude: -122.426512)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]

// Complete address as PostalAddress
print(placeMark.postalAddress as Any)  //  Import Contacts

// Location name
if let locationName = placeMark.name  {
    print(locationName)
}

// Street address
if let street = placeMark.thoroughfare {
   print(street)
}

// Country
if let country = placeMark.country {
   print(country)
}
})

可以检索更多数据

名称, 通道, subThoroughfare, 地点, subLocality, 行政区域, 子行政区域, 邮政编码, isoCountryCode, 国家, 内陆水域, areaOfInterest

于 2018-01-22T14:02:53.920 回答
0
func placePicker(_ viewController: GMSPlacePickerViewController, didPick place: GMSPlace) {

    viewController.dismiss(animated: true, completion: nil)
    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
    geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

        // Place details
        var placeMark: CLPlacemark!
        placeMark = placemarks?[0]

        // Address dictionary
        print(placeMark.addressDictionary as Any)
   // 

    print("Place name \(place.name)")
    print("Place address \(String(describing: place.formattedAddress))")
    print("Place attributions \(String(describing: place.attributions))")



})
}

使用此代码,这将解决问题。

于 2018-02-14T07:39:33.377 回答
0

在 didUpdateToLocation 方法中:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:
    (CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
    CLLocation *location = [locationManager location];


    CLLocationCoordinate2D coordinate = [location coordinate];


    latitude = [NSString stringWithFormat:@"%.12f", coordinate.latitude];
    longitude = [NSString stringWithFormat:@"%.12f", coordinate.longitude];

    CLLocation *location1 = [[CLLocation alloc]
                             initWithLatitude:latitude.floatValue
                             longitude:longitude.floatValue];

    self.myGeocoder = [[CLGeocoder alloc] init];

    [self.myGeocoder
     reverseGeocodeLocation:location1
     completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error == nil &&
             [placemarks count] > 0){
            placemark = [placemarks lastObject];
            NSString*    vendorLocation=[NSString stringWithFormat:@"%@ %@",
                                          placemark.locality,
                                          placemark.subLocality];
            NSLog(@"%@",vendorLocation);
        }
    }];
}
于 2017-06-24T09:09:57.060 回答