CLPlacemark для строки в iOS 9
Я хочу форматировать CLPlacemark
в строку.
Хорошо известный способ - использовать ABCreateStringWithAddressDictionary
, но он устарел в iOS 9. Предупреждение говорит мне вместо этого использовать CNPostalAddressFormatter
.
Однако CNPostalAddressFormatter
может форматировать только CNPostalAddress
. Невозможно правильно преобразовать CLPlacemark
в CNPostalAddress
; только эти 3 свойства совместно используются CLPlacemark
и CNPostalAddress
: country
, ISOcountryCode
и postalCode
.
Итак, как мне форматировать CLPlacemark
для строки сейчас?
Ответы
Ответ 1
Возьмите метку addressDictionary
и используйте ее ключ "FormattedAddressLines"
для извлечения строки адреса. Обратите внимание, что это массив строк строки.
(Вы правы, однако, что разработчики Apple, которым поручено преобразование в платформу контактов, похоже, полностью забыли об обмене между адресной книгой и CLPlacemark. Это серьезная ошибка в платформе контактов - одна из многих.)
РЕДАКТИРОВАТЬ Так как я опубликовал этот ответ изначально, Apple исправила эту ошибку. CLPlacemark теперь имеет свойство postalAddress
которое является CNPostalAddress, и затем вы можете использовать CNPostalAddressFormatter, чтобы получить красивую многострочную адресную строку. Обязательно import Contacts
!
Ответ 2
Swift 3.0
if let lines = myCLPlacemark.addressDictionary?["FormattedAddressLines"] as? [String] {
let placeString = lines.joined(separator: ", ")
// Do your thing
}
Ответ 3
Swift 4.1 (и 3 и 4, сохранить 1 строку)
Я прочитал вопрос, чтобы спросить: "Как я могу это реализовать?":
extension String {
init?(placemark: CLPlacemark?) {
// Yadda, yadda, yadda
}
}
Два метода
Я сначала пошел на портирование метода AddressDictionary, как и другие постеры. Но это означает потерю мощности и гибкости класса и форматера CNPostalAddress
. Следовательно, способ 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))
}
}
Подождите, что это CNPostalAddress
инициализация CLPlacemark
→ CNPostalAddress
??
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 ?? ""
}
}
}
использование
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
*/
Кто бы ни читал до здесь, получает бесплатную футболку. (на самом деле, нет)
* Этот код работает в Swift 3 и 4, за исключением того, что flatMap
для удаления значений nil flatMap
/переименован в compactMap
в Swift 4.1 (документация приведена здесь, или посмотрите SE-187 для обоснования).
Ответ 4
Метод поддержки Swift 3.0
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
}