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

Swift - CLGeocoder reverseGeocodeLocation завершение закрытия Handler

То, что я пытаюсь сделать, это передать 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 и они действительны.

ИЗМЕНИТЬ 2: После печати placemarks я могу подтвердить, что он возвращает данные. Однако p по-прежнему возвращает nil.

4b9b3361

Ответ 1

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

Проблема заключается в том, что reverseGeocodeLocation является асинхронным, метод возвращает значение перед завершениемBlock устанавливает 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 будет установлена ​​функция (должна) получить требуемую метку и выполнить нужный код. Надеюсь, что я сказал, имеет смысл.

Ответ 2

С помощью этих строк 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)
        }
    })
}

Ура!

Ответ 3

Вот закрытие, которое сработало для меня - потребовалось некоторое время, чтобы заставить его работать. Я думаю, ваша проблема связана не с инициализацией p с правильным инициализатором. Я пробовал несколько вариантов, пока не получил это: self.placemark = CLPlacemark (метка: stuff [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:

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

Ответ 4

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: empty
Внутри, что находится в p: Соединенные Штаты Америки Вне того, что находится в p: empty
Внутри, что находится в p: Соединенные Штаты Америки Вне того, что находится в p: empty...

Ответ 5

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

Основная проблема с вашим кодом заключается в том, что p (ваша метка) устанавливается после возвращения функции, поэтому она просто потеряна - вы не можете использовать функцию для возврата значения с помощью async. При завершении закрытия ваш код передается меткой, когда он прибывает (асинхронно), и замыкание вызывается - обратите внимание, что функция ничего не возвращает.

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, но он выглядит правильно...

Ответ 6

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

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
}