CLPlacemark在iOS 9中为字符串

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CLPlacemark在iOS 9中为字符串相关的知识,希望对你有一定的参考价值。

我想将CLPlacemark格式化为字符串。

众所周知的方法是使用ABCreateStringWithAddressDictionary但它在ios 9中已被弃用。警告告诉我使用CNPostalAddressFormatter代替。

但是,CNPostalAddressFormatter只能格式化CNPostalAddress。没有办法正确地将CLPlacemark转换为CNPostalAddress;只有这三个属性由CLPlacemarkCNPostalAddress共享:countryISOcountryCodepostalCode

那么我应该如何将CLPlacemark格式化为字符串呢?

答案

取地标的addressDictionary并使用其"FormattedAddressLines"密钥来提取地址字符串。请注意,这是字符串行的数组。

(但是,你是正确的,那些负责转换到Contacts框架的Apple开发人员似乎完全忘记了Address Book和CLPlacemark之间的交换。这是Contacts框架中的一个严重错误 - 很多人中的一个。)


编辑因为我最初发布了这个答案,Apple修复了这个错误。 CLPlacemark现在有一个postalAddress属性,它是一个CNPostalAddress,然后你可以使用CNPostalAddressFormatter来获得一个漂亮的多行地址字符串。一定要import Contacts

另一答案

Swift 3.0

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

Swift 4.1(和3&4,保存1行)

I read the question to ask 'How might I implement this?':

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

Two Methods

我第一次去移植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))
    }
}

等等,那是什么CLPlacemarkCNPostalAddress初始化器?

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 ?? ""
        }
    }
}

Usage

func quickAndDirtyDemo() {
    let location = CLLocation(latitude: 38.8977, longitude: -77.0365)

    CLGeocoder().reverseGeocodeLocation(location) { (placemarks, _) in
        if let address = String(depreciated: placemarks?.first) {
            print("
Address Dictionary method:
(address)") }

        if let address = String(betterMethod: placemarks?.first) {
            print("
Enumerated init method:
(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中折旧/重命名为compactMap(Doc here,或者参见SE-187的基本原理)。

另一答案

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在iOS 9中为字符串的主要内容,如果未能解决你的问题,请参考以下文章

CLPlacemark 的“名称”

如何在 Core Data 中存储 CLPlacemark 地址数据

我可以在 Xcode 9.2 中为 iOS 12 开发应用程序吗?

从人类可读的字符串创建 MKPlacemark/CLPlacemark

无法在 ios 9 中为 AVCaptureDevice 访问相机。

如何在 Swift 中为 iOS 10 和 iOS 9.3 初始化 NSManagedObject 子类