geocoder ios objective-c swift geocoding

ios - geocoder swift



CLPlacemark a cadena en iOS 9 (4)

Quiero formatear CLPlacemark a cadena.

La forma más conocida es usar ABCreateStringWithAddressDictionary pero está en desuso en iOS 9. La advertencia me dice que use CNPostalAddressFormatter en CNPostalAddressFormatter lugar.

Sin embargo, CNPostalAddressFormatter solo puede formatear CNPostalAddress . No hay forma de convertir correctamente CLPlacemark a CNPostalAddress ; solo estas 3 propiedades son compartidas por CLPlacemark y CNPostalAddress : country , ISOcountryCode y postalCode .

Entonces, ¿cómo debo formatear CLPlacemark a la cadena ahora?


Swift 3.0

if let lines = myCLPlacemark.addressDictionary?["FormattedAddressLines"] as? [String] { let placeString = lines.joined(separator: ", ") // Do your thing }


Swift 4.1 (y 3 y 4, ahorre 1 línea)

Leí la pregunta para preguntar ''¿Cómo puedo implementar esto?'':

extension String { init?(placemark: CLPlacemark?) { // Yadda, yadda, yadda } }

Dos metodos

Primero fui para portar el método AddressDictionary, al igual que otros carteles. Pero eso significa perder el poder y la flexibilidad de la clase y el formateador de CNPostalAddress . Por lo tanto, el método 2.

extension String { // original method (edited) init?(depreciated placemark1: CLPlacemark?) { // UPDATE: **addressDictionary depreciated in iOS 11** guard let myAddressDictionary = placemark1?.addressDictionary, let myAddressLines = myAddressDictionary["FormattedAddressLines"] as? [String] else { return nil } self.init(myAddressLines.joined(separator: " ")) } // my preferred method - let CNPostalAddressFormatter do the heavy lifting init?(betterMethod placemark2: CLPlacemark?) { // where the magic is: guard let postalAddress = CNMutablePostalAddress(placemark: placemark2) else { return nil } self.init(CNPostalAddressFormatter().string(from: postalAddress)) } }

Espera, ¿qué es ese CLPlacemarkCNPostalAddress initializer?

extension CNMutablePostalAddress { convenience init(placemark: CLPlacemark) { self.init() street = [placemark.subThoroughfare, placemark.thoroughfare] .compactMap { $0 } // remove nils, so that... .joined(separator: " ") // ...only if both != nil, add a space. /* // Equivalent street assignment, w/o flatMap + joined: if let subThoroughfare = placemark.subThoroughfare, let thoroughfare = placemark.thoroughfare { street = "/(subThoroughfare) /(thoroughfare)" } else { street = (placemark.subThoroughfare ?? "") + (placemark.thoroughfare ?? "") } */ city = placemark.locality ?? "" state = placemark.administrativeArea ?? "" postalCode = placemark.postalCode ?? "" country = placemark.country ?? "" isoCountryCode = placemark.isoCountryCode ?? "" if #available(iOS 10.3, *) { subLocality = placemark.subLocality ?? "" subAdministrativeArea = placemark.subAdministrativeArea ?? "" } } }

Uso

func quickAndDirtyDemo() { let location = CLLocation(latitude: 38.8977, longitude: -77.0365) CLGeocoder().reverseGeocodeLocation(location) { (placemarks, _) in if let address = String(depreciated: placemarks?.first) { print("/nAddress Dictionary method:/n/(address)") } if let address = String(betterMethod: placemarks?.first) { print("/nEnumerated init method:/n/(address)") } } } /* Output: Address Dictionary method: The White House 1600 Pennsylvania Ave NW Washington, DC 20500 United States Enumerated init method: 1600 Pennsylvania Ave NW Washington DC 20500 United States */

Quien lea hasta aquí recibe una camiseta gratis. (realmente no)

* Este código funciona en Swift 3 y 4, excepto que flatMap para eliminar valores nulos se ha depreciado / renombrado a compactMap en Swift 4.1 (Doc. here , o consulte SE-187 para la justificación).


Toma la direcciónDiccionario de la marca de posición y usa su clave "FormattedAddressLines" para extraer la cadena de dirección. Tenga en cuenta que esta es una matriz de las líneas de la cadena.

(Sin embargo, es correcto que los desarrolladores de Apple encargados de la conversión al marco de contactos parecen haberse olvidado por completo del intercambio entre la libreta de direcciones y CLPlacemark. Este es un error grave en el marco de contactos, uno de muchos).

EDITAR Desde que publiqué esa respuesta originalmente, Apple corrigió este error. Una marca CLPlacemark ahora tiene una propiedad postalAddress que es una dirección CNPostalAddress, y luego puede usar un CNPostalAddressFormatter para obtener una buena cadena de direcciones de varias líneas. Asegúrese de import Contacts !


Método Swift 3.0 Helper

class func addressFromPlacemark(_ placemark:CLPlacemark)->String{ var address = "" if let name = placemark.addressDictionary?["Name"] as? String { address = constructAddressString(address, newString: name) } if let city = placemark.addressDictionary?["City"] as? String { address = constructAddressString(address, newString: city) } if let state = placemark.addressDictionary?["State"] as? String { address = constructAddressString(address, newString: state) } if let country = placemark.country{ address = constructAddressString(address, newString: country) } return address }