可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
What I'm trying to do is pass a CLLocation
to the function getPlacemarkFromLocation
which then uses the passed CLLocation
through reverseGeocodeLocation
to set the CLPlacemark?
that will be returned.
I'm having issues creating the completionHandler
closure in reverseGeocodeLocation
, it's throwing a compiler error/crash:
In Swift, CLGeocodeCompletionHandler
is CLGeocodeCompletionHandler = (AnyObject[]!, NSError!) -> Void
according to the documentation AnyObject[]!
is supposed to contain CLPlacemark
objects just like the Objective-C version.
Here's my current code:
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: It seems like the error had to do with placemarks.count
with placemarks
not being treated like an array. It compiles now, however I'm getting nothing but nil when trying to set p
inside the completionHandler
. I've checked the CLLocation
s being passed and they are valid.
EDIT 2: After printing placemarks
, I can confirm that it returns data. However p
is still returning nil.
回答1:
I found the answer I needed in this thread: Set address string with reverseGeocodeLocation: and return from method
The issue lies with the fact that reverseGeocodeLocation
is asynchronous, the method is returning a value before the completionBlock sets p
in my example.
As requested, here's my current code.
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) }
})
}
I didn't want to take the NSNotificationCenter route because that would add unnecessary overhead, rather inside the completionHandler
closure I call upon another function and pass the CLPlacemark
generated by getPlacemarkFromLocation
as a parameter to keep things asynchronous since the function will be called after placemarks
is set the function (should) receive the placemark needed and execute the code you want. Hope what I said makes sense.
回答2:
With these lines of Swift, you can print out fully the location's address:
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)
}
})
}
Cheers!
回答3:
Here is closure that worked for me -- it took awhile to get it to work. I think your problem is related to not initializing p with the correct initializer. I tried a few variations until I got this to work: self.placemark = CLPlacemark(placemark: stuff[0] as 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:
moved better answer to its own answer.
回答4:
EDIT: This doesn't work. The value is nil outside the closure -- see comments below
Your p is nil because the closure is capturing it before it is initialized to a reference. To get the behavior you want you need to make p a non-optional value such as var p : CLPlacemark!.
Below is code I used to test my conjecture:
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)")
}
Here is console log:
Pushit <- button pressed to start location capturing
Outside what is in p: empty
Inside what is in p: United States
Outside what is in p: empty
Inside what is in p: United States
Outside what is in p: empty...
回答5:
Bit late to this party, but it looks like you need(ed) to do some ground-up reading about async stuff. Saying that, you've probably learnt it by now.
The basic problem with your code is that p (your placemark) is being set after the function returns, so it's just lost - you can't use a function to return a value with async. With a completion closure, your code is passed the placemark when it arrives (asynchronously) & the closure is invoked - note the function is now returning nothing.
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)
})
}
Use -
getPlacemarkFromLocation(myLocation, completion: { (placemark) in
// do something with the placemark here
})
I've not actually put this into Xcode, but it looks right...
回答6:
Your stuff doesn't work for a number of reasons. Here's the part that I fixed without actually looking at the functionality:
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
}