CLPlacemark 到 iOS 9 中的字符串
CLPlacemark to string in iOS 9
我想将 CLPlacemark
格式化为字符串。
众所周知的方法是使用 ABCreateStringWithAddressDictionary
但它在 iOS 9 中已被弃用。警告告诉我改用 CNPostalAddressFormatter
。
但是CNPostalAddressFormatter
只能格式化CNPostalAddress
。无法将 CLPlacemark
正确转换为 CNPostalAddress
;只有这 3 个属性由 CLPlacemark
和 CNPostalAddress
共享:country
、ISOcountryCode
和 postalCode
.
那么我现在应该如何将 CLPlacemark
格式化为字符串?
获取地标的 addressDictionary
并使用其 "FormattedAddressLines"
键提取地址字符串。请注意,这是字符串行的 数组 。
(但是,您是对的,负责转换为联系人框架的 Apple 开发人员似乎完全忘记了地址簿和 CLPlacemark 之间的互换。这是联系人框架中的一个严重错误 - 许多错误之一.)
编辑 因为我最初发布了那个答案,Apple 修复了这个错误。 CLPlacemark 现在有一个 postalAddress
属性,它是一个 CNPostalAddress,然后您可以使用 CNPostalAddressFormatter 来获得一个漂亮的多行地址字符串。务必import Contacts
!
Swift 4.1(和 3 & 4,省 1 行)
我看了要问的问题'How might I implement this?':
extension String {
init?(placemark: CLPlacemark?) {
// Yadda, yadda, yadda
}
}
两种方法
我和其他发帖人一样,首先着手移植 AddressDictionary 方法。但这意味着失去 CNPostalAddress
class 和格式化程序的强大功能和灵活性。因此,方法 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))
}
}
等等,CLPlacemark
→ CNPostalAddress
初始化器是什么?
extension CNMutablePostalAddress {
convenience init(placemark: CLPlacemark) {
self.init()
street = [placemark.subThoroughfare, placemark.thoroughfare]
.compactMap { [=12=] } // 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
*/
读到这里的人将获得一件免费的 T 恤。 (不是真的)
*此代码在 Swift 3 和 4 中有效,除了用于删除 nil 值的 flatMap
在 Swift 4.1 中已从 depreciated/renamed 到 compactMap
(文档 here, or see SE-187 的基本原理)。
Swift 3.0
if let lines = myCLPlacemark.addressDictionary?["FormattedAddressLines"] as? [String] {
let placeString = lines.joined(separator: ", ")
// Do your thing
}
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
}
我想将 CLPlacemark
格式化为字符串。
众所周知的方法是使用 ABCreateStringWithAddressDictionary
但它在 iOS 9 中已被弃用。警告告诉我改用 CNPostalAddressFormatter
。
但是CNPostalAddressFormatter
只能格式化CNPostalAddress
。无法将 CLPlacemark
正确转换为 CNPostalAddress
;只有这 3 个属性由 CLPlacemark
和 CNPostalAddress
共享:country
、ISOcountryCode
和 postalCode
.
那么我现在应该如何将 CLPlacemark
格式化为字符串?
获取地标的 addressDictionary
并使用其 "FormattedAddressLines"
键提取地址字符串。请注意,这是字符串行的 数组 。
(但是,您是对的,负责转换为联系人框架的 Apple 开发人员似乎完全忘记了地址簿和 CLPlacemark 之间的互换。这是联系人框架中的一个严重错误 - 许多错误之一.)
编辑 因为我最初发布了那个答案,Apple 修复了这个错误。 CLPlacemark 现在有一个 postalAddress
属性,它是一个 CNPostalAddress,然后您可以使用 CNPostalAddressFormatter 来获得一个漂亮的多行地址字符串。务必import Contacts
!
Swift 4.1(和 3 & 4,省 1 行)
我看了要问的问题'How might I implement this?':
extension String {
init?(placemark: CLPlacemark?) {
// Yadda, yadda, yadda
}
}
两种方法
我和其他发帖人一样,首先着手移植 AddressDictionary 方法。但这意味着失去 CNPostalAddress
class 和格式化程序的强大功能和灵活性。因此,方法 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))
}
}
等等,CLPlacemark
→ CNPostalAddress
初始化器是什么?
extension CNMutablePostalAddress {
convenience init(placemark: CLPlacemark) {
self.init()
street = [placemark.subThoroughfare, placemark.thoroughfare]
.compactMap { [=12=] } // 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
*/
读到这里的人将获得一件免费的 T 恤。 (不是真的)
*此代码在 Swift 3 和 4 中有效,除了用于删除 nil 值的 flatMap
在 Swift 4.1 中已从 depreciated/renamed 到 compactMap
(文档 here, or see SE-187 的基本原理)。
Swift 3.0
if let lines = myCLPlacemark.addressDictionary?["FormattedAddressLines"] as? [String] {
let placeString = lines.joined(separator: ", ")
// Do your thing
}
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
}