Swift-CLGeocoder reverseGeocodeLocation completionHandler закрытие


То, что я пытаюсь сделать, это передать CLLocation в функцию getPlacemarkFromLocation, которая затем использует переданный CLLocation через reverseGeocodeLocation, чтобы установить CLPlacemark?, который будет возвращен.

У меня возникли проблемы с созданием completionHandler закрытия в reverseGeocodeLocation, это вызывает ошибку компилятора / сбой:


В Swift, CLGeocodeCompletionHandler является CLGeocodeCompletionHandler = (AnyObject[]!, NSError!) -> Void согласно документации AnyObject[]! должен содержать CLPlacemark объекты так же, как версия Objective-C.

Вот мой текущий код:

class func getPlacemarkFromLocation(location:CLLocation)->CLPlacemark?{
    var g = CLGeocoder()
    var p:CLPlacemark?
    g.reverseGeocodeLocation(location, completionHandler: {
        (placemarks, error) in
        let pm = placemarks as? CLPlacemark[]
        if (pm && pm?.count > 0){
            p = placemarks[0] as? CLPlacemark
        }
    })
    return p?
}

EDIT: похоже, что ошибка связана с тем, что placemarks.count С placemarks не обрабатывается как массив. Он компилируется сейчас, однако я не получаю ничего, кроме нуля, когда пытаюсь установить p внутри completionHandler. Я проверил, что CLLocationпередаются, и они действительны.

EDIT 2: После печати placemarks я могу подтвердить, что он возвращает данные. Однако p по-прежнему возвращает ноль.

6 21

6 ответов:

Я нашел ответ, который мне был нужен в этом потоке: задайте адресную строку с обратным размещением: и возвращением из метода

Проблема заключается в том, что reverseGeocodeLocation является асинхронным, метод возвращает значение до завершения набора блоков p в моем примере.


Как я и просил, вот мой текущий код.

func showAddViewController(placemark:CLPlacemark){
    self.performSegueWithIdentifier("add", sender: placemark) 
}

func getPlacemarkFromLocation(location: CLLocation){
    CLGeocoder().reverseGeocodeLocation(location, completionHandler:
        {(placemarks, error) in
            if error {println("reverse geodcode fail: \(error.localizedDescription)")}
            let pm = placemarks as [CLPlacemark]
            if pm.count > 0 { self.showAddPinViewController(placemarks[0] as CLPlacemark) }
    })
}

Я не хотел брать маршрут NSNotificationCenter, потому что это добавило бы ненужные накладные расходы, а не внутри completionHandler закрытия, которое я вызываю на другую функцию и передайте CLPlacemark, сгенерированный getPlacemarkFromLocation в качестве параметра, чтобы сохранить вещи асинхронными, так как функция будет вызвана после того, как placemarks будет установлена функция (должна) получить необходимую метку и выполнить код, который вы хотите. Надеюсь, то, что я сказал, имеет смысл.

С помощью этих строк Swift вы можете полностью распечатать адрес местоположения:

func getLocationAddress(location:CLLocation) {
    var geocoder = CLGeocoder()

    println("-> Finding user address...")

    geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error)->Void in
        var placemark:CLPlacemark!

        if error == nil && placemarks.count > 0 {
            placemark = placemarks[0] as CLPlacemark

            var addressString : String = ""
            if placemark.ISOcountryCode == "TW" /*Address Format in Chinese*/ {
                if placemark.country != nil {
                    addressString = placemark.country
                }
                if placemark.subAdministrativeArea != nil {
                    addressString = addressString + placemark.subAdministrativeArea + ", "
                }
                if placemark.postalCode != nil {
                    addressString = addressString + placemark.postalCode + " "
                }
                if placemark.locality != nil {
                    addressString = addressString + placemark.locality
                }
                if placemark.thoroughfare != nil {
                    addressString = addressString + placemark.thoroughfare
                }
                if placemark.subThoroughfare != nil {
                    addressString = addressString + placemark.subThoroughfare
                }
            } else {
                if placemark.subThoroughfare != nil {
                    addressString = placemark.subThoroughfare + " "
                }
                if placemark.thoroughfare != nil {
                    addressString = addressString + placemark.thoroughfare + ", "
                }
                if placemark.postalCode != nil {
                    addressString = addressString + placemark.postalCode + " "
                }
                if placemark.locality != nil {
                    addressString = addressString + placemark.locality + ", "
                }
                if placemark.administrativeArea != nil {
                    addressString = addressString + placemark.administrativeArea + " "
                }
                if placemark.country != nil {
                    addressString = addressString + placemark.country
                }
            }

            println(addressString)
        }
    })
}

Ура!

Вот завершение, которое сработало для меня - потребовалось некоторое время, чтобы заставить его работать. Я думаю, что ваша проблема связана с тем, что вы не инициализируете p с помощью правильного инициализатора. Я попробовал несколько вариантов, пока у меня не получилось: self.placemark = CLPlacemark (метка: материал[0] как CLPlacemark)

geocoder.reverseGeocodeLocation(newLocation, completionHandler: {(stuff, error)->Void in

        if error {
            println("reverse geodcode fail: \(error.localizedDescription)")
            return
        }

        if stuff.count > 0 {
            self.placemark = CLPlacemark(placemark: stuff[0] as CLPlacemark)

            self.addressLabel.text = String(format:"%@ %@\n%@ %@ %@\n%@",
                self.placemark.subThoroughfare ? self.placemark.subThoroughfare : "" ,
                self.placemark.thoroughfare ? self.placemark.thoroughfare : "",
                self.placemark.locality ? self.placemark.locality : "",
                self.placemark.postalCode ? self.placemark.postalCode : "",
                self.placemark.administrativeArea ? self.placemark.administrativeArea : "",
                self.placemark.country ? self.placemark.country : "")
        }
        else {
            println("No Placemarks!")
            return
        }

        })

Правка:

Переместил лучший ответ на свой собственный ответ.

EDIT: это не работает. Значение равно нулю вне закрытия - см. комментарии ниже

Ваш p равен нулю, потому что закрытие захватывает его до инициализации в ссылку. Чтобы получить желаемое поведение, вам нужно сделать p необязательным значением, таким как var p : CLPlacemark!.

Ниже приведен код, который я использовал для проверки своей гипотезы:

 func locationManager(manager: CLLocationManager!, didUpdateToLocation newLocation: CLLocation!, fromLocation oldLocation: CLLocation!) {
    var g = CLGeocoder()
    var p:CLPlacemark?
    let mynil = "empty"
    g.reverseGeocodeLocation(newLocation, completionHandler: {
        (placemarks, error) in
        let pm = placemarks as? CLPlacemark[]
        if (pm && pm?.count > 0){
           // p = CLPlacemark()
            p = CLPlacemark(placemark: pm?[0] as CLPlacemark)

            println("Inside what is in p: \(p?.country ? p?.country : mynil)")

        }

        })

    println("Outside what is in p: \(p?.country ? p?.country : mynil)")

}

Вот журнал консоли:

Pushit Вне того, что находится в p: пусто
Внутри что находится в p: Соединенные Штаты
Вне того, что находится в p: пусто
Внутри того, что находится в p: Соединенные Штаты
Снаружи то, что в р: пусто...

Немного опоздал на эту вечеринку, но похоже, что вам нужно(ed) сделать некоторые начальные чтения об асинхронных вещах. Говоря это, вы, вероятно, уже научились этому.

Основная проблема с вашим кодом заключается в том, что p (ваша метка) устанавливается после возврата функции, поэтому она просто теряется - вы не можете использовать функцию для возврата значения с асинхронностью. С завершением закрытия, ваш код передается метку, когда он прибывает (асинхронно) и закрытие вызывается-обратите внимание, что функция теперь ничего не возвращая.
func getPlacemarkFromLocation(_ location: CLLocation, completion: ((CLPlacemark?) -> ())) {

    CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in
        // use optional chaining to safely return a value or nil
        // also using .first rather than checking the count & getting placemarks[0] -
        // if the count is zero, will just give you nil
        // probably a good idea to check for errors too
        completion(placemarks?.first)
    })
}

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

getPlacemarkFromLocation(myLocation, completion: { (placemark) in
    // do something with the placemark here
})

На самом деле я не помещал это в Xcode, но это выглядит правильно...

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

class func getPlacemarkFromLocation(location:CLLocation)->CLPlacemark?{
    var g = CLGeocoder()
    var p:CLPlacemark?
    g.reverseGeocodeLocation(location, completionHandler: {
        (placemarks, error) in
        let pm = placemarks!
        if (pm.count > 0){
            p = placemarks![0]
        }
    })
    return p
}