Swift-создание формата адреса из обратного геокодирования

Я пытаюсь создать отформатированный полный адрес с помощью CLGeocoder в Swift 8. Я говорил об этом так нить, чтобы получить код, приведенный ниже.

однако иногда приложение падает с ошибкой " nil " в строке:

//Address dictionary
print(placeMark.addressDictionary ?? "")

вопросы:

  1. как я могу объединить эти значения, полученные из геокодера, чтобы сформировать полный адрес? (Улица + город + etc)
  2. как мне справиться с нулевой ошибкой, которую я получаю, когда func не может найти адрес?

полный код:

func getAddress() -> String {
        var address: String = ""

        let geoCoder = CLGeocoder()
        let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
        //selectedLat and selectedLon are double values set by the app in a previous process

        geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

            // Place details
            var placeMark: CLPlacemark!
            placeMark = placemarks?[0]

            // Address dictionary
            //print(placeMark.addressDictionary ?? "")

            // Location name
            if let locationName = placeMark.addressDictionary!["Name"] as? NSString {
                //print(locationName)
            }

            // Street address
            if let street = placeMark.addressDictionary!["Thoroughfare"] as? NSString {
                //print(street)
            }

            // City
            if let city = placeMark.addressDictionary!["City"] as? NSString {
                //print(city)
            }

            // Zip code
            if let zip = placeMark.addressDictionary!["ZIP"] as? NSString {
                //print(zip)
            }

            // Country
            if let country = placeMark.addressDictionary!["Country"] as? NSString {
                //print(country)
            }

        })

        return address;
    } 

7 ответов


func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
        var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
        let lat: Double = Double("\(pdblLatitude)")!
        //21.228124
        let lon: Double = Double("\(pdblLongitude)")!
        //72.833770
        let ceo: CLGeocoder = CLGeocoder()
        center.latitude = lat
        center.longitude = lon

        let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)


        ceo.reverseGeocodeLocation(loc, completionHandler:
            {(placemarks, error) in
                if (error != nil)
                {
                    print("reverse geodcode fail: \(error!.localizedDescription)")
                }
                let pm = placemarks! as [CLPlacemark]

                if pm.count > 0 {
                    let pm = placemarks![0]
                    print(pm.country)
                    print(pm.locality)
                    print(pm.subLocality)
                    print(pm.thoroughfare)
                    print(pm.postalCode)
                    print(pm.subThoroughfare)
                    var addressString : String = ""
                    if pm.subLocality != nil {
                        addressString = addressString + pm.subLocality! + ", "
                    }
                    if pm.thoroughfare != nil {
                        addressString = addressString + pm.thoroughfare! + ", "
                    }
                    if pm.locality != nil {
                        addressString = addressString + pm.locality! + ", "
                    }
                    if pm.country != nil {
                        addressString = addressString + pm.country! + ", "
                    }
                    if pm.postalCode != nil {
                        addressString = addressString + pm.postalCode! + " "
                    }


                    print(addressString)
              }
        })

    }

Это мой код 4 swift 3

func getAdressName(coords: CLLocation) {

    CLGeocoder().reverseGeocodeLocation(location) { (placemark, error) in
            if error != nil {

                print("Hay un error")

            } else {

                let place = placemark! as [CLPlacemark]

                if place.count > 0 {
                    let place = placemark![0]

                    var adressString : String = ""

                    if place.thoroughfare != nil {
                        adressString = adressString + place.thoroughfare! + ", "
                    }
                    if place.subThoroughfare != nil {
                        adressString = adressString + place.subThoroughfare! + "\n"
                    }
                    if place.locality != nil {
                        adressString = adressString + place.locality! + " - "
                    }
                    if place.postalCode != nil {
                        adressString = adressString + place.postalCode! + "\n"
                    }
                    if place.subAdministrativeArea != nil {
                        adressString = adressString + place.subAdministrativeArea! + " - "
                    }
                    if place.country != nil {
                        adressString = adressString + place.country!
                    }

                    self.lblPlace.text = adressString
                }

            }
        }
  }

Вы можете:

let cityCoords = CLLocation(latitude: newLat, longitude: newLon)
cityData(coord: cityCoords)

для объединения вы можете просто заменить return address это :

return "\(locationName), \(street), \(city), \(zip), \(country)"

  1. для устранения проблемы с пустым адресом вы можете использовать свойство класса для хранения добавленного значения или использовать закрытие, чтобы вернуть значение вызывающей функции
  2. для фиксации аварии вам нужно, чтобы избежать силы разворачивания optionals

используя закрытие, вы можете сделать это так:

// Using closure
func getAddress(handler: (String) -> Void)
{
    var address: String = ""
    let geoCoder = CLGeocoder()
    let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
    //selectedLat and selectedLon are double values set by the app in a previous process

    geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in

        // Place details
        var placeMark: CLPlacemark?
        placeMark = placemarks?[0]

        // Address dictionary
        //print(placeMark.addressDictionary ?? "")

        // Location name
        if let locationName = placeMark?.addressDictionary?["Name"] as? String {
            address += locationName + ", "
        }

        // Street address
        if let street = placeMark?.addressDictionary?["Thoroughfare"] as? String {
            address += street + ", "
        }

        // City
        if let city = placeMark?.addressDictionary?["City"] as? String {
            address += city + ", "
        }

        // Zip code
        if let zip = placeMark?.addressDictionary?["ZIP"] as? String {
            address += zip + ", "
        }

        // Country
        if let country = placeMark?.addressDictionary?["Country"] as? String {
            address += country
        }

       // Passing address back
       handler(address)
    })
}

вы можете вызвать метод, как:

getAddress { (address) in
    print(address)
}

Keeping it simple - полный Swift 3 & 4 совместимый пример контроллера просмотра для получения форматированной адресной строки из местоположения пользователя (добавьте другие ключи, доступные в CLPlacemark Если вы хотите получить больше информации в строке):

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

let manager = CLLocationManager()
let geocoder = CLGeocoder()

var locality = ""
var administrativeArea = ""
var country = ""

override func viewDidLoad() {
    super.viewDidLoad()

    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.requestWhenInUseAuthorization()
    manager.startUpdatingLocation()

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let location = locations[0]
        manager.stopUpdatingLocation()

    geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in
        if (error != nil) {
            print("Error in reverseGeocode")
            }

        let placemark = placemarks! as [CLPlacemark]
        if placemark.count > 0 {
            let placemark = placemarks![0]
            self.locality = placemark.locality!
            self.administrativeArea = placemark.administrativeArea!
            self.country = placemark.country!
        }
    })
}

func userLocationString() -> String {
    let userLocationString = "\(locality), \(administrativeArea), \(country)"
    return userLocationString
}

}

вызов print(userLocationString()) в этом примере будет печатать: suburb, state, country

не забудьте добавить Конфиденциальность-Расположение При Использовании Описание Использования в свой Информация.plist файл заранее, чтобы позволить пользователю предоставлять разрешения для вашего приложения, чтобы использовать службы определения местоположения.


адреса форматирования трудно, потому что каждая страна имеет свой собственный формат. Пусть Apple сделает это:

начиная с iOS 11, Вы можете получить адрес рамки контактов:

extension CLPlacemark {
    @available(iOS 11.0, *)
    open var postalAddress: CNPostalAddress? { get }
}

это расширение является частью Contacts основы. Это означает, что эта функция невидима для вас в XCode завершение кода, пока вы не сделаете

import Contacts

С этим addintional импорт, вы можете сделать что-то вроде

CLGeocoder().reverseGeocodeLocation(location, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
    guard let place = clPlacemark?.first else {
        print("No placemark from Apple: \(String(describing: error))")
        return
    }

    let postalAddressFormatter = CNPostalAddressFormatter()
    postalAddressFormatter.style = .mailingAddress
    var addressString: String?
    if let postalAddress = place.postalAddress {
        addressString = postalAddressFormatter.string(from: postalAddress)
    }
}

и получить адрес, отформатированный в формате для страна в адресе.

форматер даже поддерживает форматирование как attributedString.

до iOS 11 вы можете конвертировать CLPlacemark to CNPostalAddress вы и по-прежнему можете использовать форматирование конкретной страны CNPostalAddressFormatter.


CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: vehicleLocation.latitude, longitude: vehicleLocation.latitude), completionHandler: {(placemarks, error) -> Void in

  guard error == nil else {completionHandler(nil); return}

  guard let place = placemarks else {completionHandler(nil); return}

  if place.count > 0 {
    let pm = place[0]

    var addArray:[String] = []
    if let name = pm.name {
      addArray.append(name)
    }
    if let thoroughfare = pm.thoroughfare {
      addArray.append(thoroughfare)
    }
    if let subLocality = pm.subLocality {
      addArray.append(subLocality)
    }
    if let locality = pm.locality {
      addArray.append(locality)
    }
    if let subAdministrativeArea = pm.subAdministrativeArea {
      addArray.append(subAdministrativeArea)
    }
    if let administrativeArea = pm.administrativeArea {
      addArray.append(administrativeArea)
    }
    if let country = pm.country {
      addArray.append(country)
    }
    if let postalCode = pm.postalCode {
      addArray.append(postalCode)
    }

    let addressString = addArray.joined(separator: ",\n")

    print(addressString)

    completionHandler(addressString)
  }
  else { completionHandler(nil)}
})