Подтвердить что ты не робот

CLPlacemark для строки в iOS 9

Я хочу форматировать CLPlacemark в строку.

Хорошо известный способ - использовать ABCreateStringWithAddressDictionary, но он устарел в iOS 9. Предупреждение говорит мне вместо этого использовать CNPostalAddressFormatter.

Однако CNPostalAddressFormatter может форматировать только CNPostalAddress. Невозможно правильно преобразовать CLPlacemark в CNPostalAddress; только эти 3 свойства совместно используются CLPlacemark и CNPostalAddress: country, ISOcountryCode и postalCode.

Итак, как мне форматировать CLPlacemark для строки сейчас?

4b9b3361

Ответ 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 инициализация 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 ?? ""
        }
    }
}

использование

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
      }