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 ответов:
Я нашел ответ, который мне был нужен в этом потоке: задайте адресную строку с обратным размещением: и возвращением из метода
Проблема заключается в том, что
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 }