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 CLPlacemark
→ CNPostalAddress
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
}