Exordium

В предыдущем посте я писал о настройке Core Location в приложении UIKit. Я рассмотрел добавление описаний использования в файл Info.plist, запрос авторизации местоположения и получение местоположения из CLLocationManager. Если вам нужна помощь в настройке Core Location в вашем приложении UIKit, взгляните на этот пост: https://rustynailsoftware.com/dev-blog/core-location-setting-up-core-location-with-uikit.

В этом посте я расскажу, как реализовать CLGeocoder - класс в Core Location, который помогает разработчикам создавать удобочитаемые версии географических координат в своих приложениях для iOS. Я также вкратце расскажу об объекте CLLocation, поскольку я не смог этого сделать в своей первой публикации Core Location. Вы можете следовать за кодом, который я разместил на GitHub: https://github.com/andrew-lundy/core-location-tutorial

Давайте погрузимся.

Класс CLLocation

Важным аспектом Core Location является класс CLLocation. Это настолько важно, что я забыл написать об этом в моем первом блоге из серии Core Location. Класс CLLocation - это объект, содержащий информацию о местоположении устройства, включая информацию о высоте и курсе. Информация о курсе - это скорость и направление устройства. В Core Location вы получаете подробные сведения о местоположении через класс CLLocationManager. Здесь я сохранил информацию в переменной currentLocation:

let locationManager = CLLocationManager()
let currentLocation = locationManager.location

Благодаря этому мы получили доступ к деталям местоположения и возможность их извлекать. Например, чтобы получить координаты местоположения, используйте атрибут координаты диспетчера местоположения:

let locationManager = CLLocationManager()
let currentLocation = locationManager.location
// Print the location details.
// Ex: <+37.78735352, -122.40822700> +/- 5.00m (speed - 1.99 mps / course - 1.00) @ 9/5/20, 5:13:46 PM Central Daylight Time
print(currentLocation)
let locationCoordinate = currentLocation.coordinate
// Print the coordinate value of the location as a CLLocationCoordinate2D.
// Ex: CLLocationCoordinate2D(latitude: 37.787353515625, longitude: -122.408227)
print(locationCoordinate)

Обратное геокодирование с помощью CLGeocoder

Как видно выше, класс CLLocation возвращает информацию о местоположении в практически непригодном для использования формате. Конечно, мы можем получить географические координаты и скорость, с которой движется устройство. Но эта информация может быть полезна только в определенных ситуациях. Что происходит, когда нам нужно отображать информацию о местоположении для пользователей, которые не хотят читать и преобразовывать координаты? Ответ можно найти в классе Apple CLGeocoder.

На данный момент класс ViewController содержит следующие объекты и IBOutlets:

@IBOutlet weak var changeLocationBttn: UIButton!
@IBOutlet weak var reverseGeocodeLocation: UIButton!
@IBOutlet weak var locationDataLbl: UILabel!
private var locationManager: CLLocationManager!
private var currentLocation: CLLocation!
private var geocoder: CLGeocoder!

Я собираюсь инициализировать CLGeocoder в методе viewDidLoad класса ViewController:

override func viewDidLoad() {
  super.viewDidLoad()
  changeLocationBttn.layer.cornerRadius = 10
  reverseGeocodeLocation.layer.cornerRadius = 10
  reverseGeocodeLocation.titleLabel?.textAlignment = .center
  
  locationManager = CLLocationManager()
  locationManager.delegate = self
  
  // Initialize the Geocoder
  geocoder = CLGeocoder()
}

Вся работа, которую будет выполнять CLGeocoder, будет выполняться в методе reverseGeocodeLocationBttnTapped. Первое, что мы собираемся сделать, это убедиться, что переменная currentLocation не пуста. Он настроен для хранения информации о местоположении устройства и получает значение, когда приложение запрашивает статус авторизации. Нам нужно выполнить эту проверку, потому что нет ничего, что нужно было бы геокодировать в обратном направлении, если нет значения местоположения.

@IBAction func reverseGeocodeLocationBttnTapped(_ sender: Any) {
    guard let currentLocation = self.currentLocation else {
        print("Unable to reverse-geocode location.")
        return
    }
}

Чтобы запустить процесс обратного геокодирования координат, необходимо вызвать метод reverseGeocodeLocation в CLGeocoder. Этот метод принимает два параметра - объект CLLocation и CLGeocodeCompletionHandler. Обработчик завершения также имеет два параметра - массив CLPlacemark и Ошибка.

// The method that does the reverse-geocoding.
geocoder.reverseGeocodeLocation(location: CLLocation, completionHandler: CLGeocodeCompletionHandler)
// Here is the method when in use.
geocoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in
  
}

Класс CLPlacemark появился недавно, поэтому давайте взглянем на него. CLPlacemark, или метка, содержит удобочитаемую версию координат и дает разработчикам доступ к такой информации, как название места, город, штат, почтовый индекс и т. Д. Вы можете узнать больше о классе CLPlacemark в документации Apple: https://developer.apple.com/documentation/corelocation/clplacemark

Вот шаги, которые мы выполним в обработчике завершения:

geocoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in
    // 1
    if let error = error {
        print(error)
    }
            
    // 2
    guard let placemark = placemarks?.first else { return }
    print(placemark)
    // Geary & Powell, Geary & Powell, 299 Geary St, San Francisco, CA 94102, United States @ <+37.78735352,-122.40822700> +/- 100.00m, region CLCircularRegion (identifier:'<+37.78735636,-122.40822737> radius 70.65', center:<+37.78735636,-122.40822737>, radius:70.65m)
            
    // 3
    guard let streetNumber = placemark.subThoroughfare else { return }
    guard let streetName = placemark.thoroughfare else { return }
    guard let city = placemark.locality else { return }
    guard let state = placemark.administrativeArea else { return }
    guard let zipCode = placemark.postalCode else { return }
            
    // 4
    DispatchQueue.main.async {
        self.locationDataLbl.text = "\(streetNumber) \(streetName) \n \(city), \(state) \(zipCode)"
    }
}
  1. Проверьте, не выдает ли обработчик ошибку. Если да, распечатайте его на консоли. В реальном приложении вы справитесь с ошибкой более эффективно.
  2. Используйте оператор защиты, чтобы получить первую метку, возвращенную обработчиком завершения. Для большинства запросов геокодирования массив меток должен содержать только одну запись. Я пошел дальше и распечатал данные метки.
  3. В зависимости от варианта использования извлекайте определенные данные из метки. В данном случае я вытащил номер улицы, название улицы, город, штат и почтовый индекс.
  4. Наконец, я обновил метку в приложении данными метки. Поскольку это меняет пользовательский интерфейс, я сделал это в основном потоке, используя класс DispatchQueue.

IBAction reverseGeocodeLocationBttnTapped теперь должен выглядеть следующим образом:

@IBAction func reverseGeocodeLocationBttnTapped(_ sender: Any) {
    guard let currentLocation = self.currentLocation else {
        print("Unable to reverse-geocode location.")
        return
    }
    
    geocoder.reverseGeocodeLocation(currentLocation) { (placemarks, error) in
        if let error = error {
            print(error)
        }
            
        guard let placemark = placemarks?.first else { return }
        guard let streetNumber = placemark.subThoroughfare else { return }
        guard let streetName = placemark.thoroughfare else { return }
        guard let city = placemark.locality else { return }
        guard let state = placemark.administrativeArea else { return }
        guard let zipCode = placemark.postalCode else { return }
  
        DispatchQueue.main.async {
            self.locationDataLbl.text = "\(streetNumber) \(streetName) \n \(city), \(state) \(zipCode)"
        }
    } 
}

Заканчивать

Приложение теперь может запрашивать авторизацию местоположения, получать местоположение устройства и - новейшая функция - выполнять обратное геокодирование местоположения. Вот как выглядит приложение:

Спасибо, что уделили немного времени своему дню и прочитали это пошаговое руководство. Core Location - это ключ к созданию любого приложения для iOS, основанного на местоположении, и я надеюсь, что вы узнали что-то новое. Не стесняйтесь связаться со мной в Twitter и помочь развитию сообщества разработчиков iOS: https://twitter.com/andrewlundydev.

Вы можете прочитать исходную версию в моем блоге: https://rustynailsoftware.com/dev-blog/core-location-reverse-geocoding-locations-using-clgeocoder

Ресурсы:

Настройка основного местоположения с помощью UIKit: https://rustynailsoftware.com/dev-blog/core-location-setting-up-core-location-with-uikit

CLLocation Docs: https://developer.apple.com/documentation/corelocation/cllocation

Документы CLGeocoder: https://developer.apple.com/documentation/corelocation/clgeocoder

Документы CLGeocodeCompletionHandler: https://developer.apple.com/documentation/corelocation/clgeocodecompletionhandler

Документы CLPlacemark: https://developer.apple.com/documentation/corelocation/clplacemark

Документы DispatchQueue: https://developer.apple.com/documentation/dispatch/dispatchqueue