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.
Ответы
Ответ 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
}