2014-06-21 17 views
21

Quello che sto cercando di fare è passare un CLLocation alla funzione getPlacemarkFromLocation che poi utilizza il passato CLLocation attraverso reverseGeocodeLocation per impostare il CLPlacemark? che verrà restituito.Swift - CLGeocoder reverseGeocodeLocation chiusura completionHandler

sto avendo problemi che creano il completionHandler chiusura nel reverseGeocodeLocation, è gettando un errore di compilazione/crash:


In Swift, CLGeocodeCompletionHandler è CLGeocodeCompletionHandler = (AnyObject[]!, NSError!) -> Void in base alla documentazione AnyObject[]! si suppone che contengono CLPlacemark oggetti proprio come la versione Objective-C.

Ecco il mio codice corrente:

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: Sembra che l'errore ha avuto a che fare con placemarks.count con placemarks non essere trattati come un array. Compilano ora, tuttavia non ottengo nulla se non zero quando cerco di impostare p all'interno dello completionHandler. Ho controllato il passaggio degli CLLocation e sono validi.

MODIFICA 2: Dopo aver stampato placemarks, posso confermare che restituisce i dati. Tuttavia, p restituisce ancora zero.

risposta

20

ho trovato la risposta che serviva in questa discussione: Set address string with reverseGeocodeLocation: and return from method

Il problema risiede nel fatto che reverseGeocodeLocation è asincrona, il metodo restituisce un valore prima del completionBlock imposta p nel mio esempio.


Come richiesto, ecco il mio codice corrente.

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) } 
    }) 
} 

non ho voluto prendere la strada NSNotificationCenter perché sarebbe aggiungere un inutile sovraccarico, piuttosto all'interno della completionHandler chiusura Invito un'altra funzione e passare il CLPlacemark generato da getPlacemarkFromLocation come parametro per mantenere le cose asincroni in quanto la la funzione verrà richiamata dopo che placemarks è impostata sulla funzione (dovrebbe) ricevere il segnaposto necessario ed eseguire il codice desiderato. Spero che ciò che ho detto abbia senso.

+0

Puoi approfondire come hai risolto il problema? Ho provato funzioni e notifiche esterne e sto ancora ricevendo EXC_BAD_ACCESS. Grazie in anticipo, questo aiuterebbe gli altri sviluppatori a passare a Swift. – pxpgraphics

+0

scusate, stavo usando questa sintassi ... 'self! .geocoder.reverseGeocodeLocation (location, completionHandler: {[self debole] (segnaposto: [AnyObject] !, errore: NSError!) -> Void in // Your }) ' – pxpgraphics

+0

Certo, modificherei questo post e aggiungerò il mio codice corrente ed elaborerò un po 'di più. – AaronDancer

3

Ecco la chiusura che ha funzionato per me: ci è voluto un po 'per farlo funzionare. Penso che il tuo problema sia legato a non inizializzare p con l'inizializzatore corretto. Ho provato un paio di varianti finché non ho avuto questo lavoro: self.placemark = CLPlacemark (segnaposto: roba [0] come 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:

trasferito migliore risposta alla propria risposta.

+0

Sembra come 'p' non essere inizializzato è il problema, quindi ho cambiato 'p = segnaposto [0] come? CLPlacemark' a 'p = CLPlacemark (segnaposto: segnaposto [0] come? CLPlacemark)' tuttavia p restituisce ancora nil. Ho provato a inizializzare 'p' prima che venga chiamato' reverseGeocodeLocation', tuttavia ottengo EXC_BAD_ACCESS quando provo a cambiare il valore. Non riesco ad usare in anticipo l'inizializzatore 'CLPlacemark (segnaposto: CLPlacemark?) Perché non ho' CLPlacemark' per dare – AaronDancer

0

MODIFICA: questo non funziona. Il valore è nullo al di fuori della chiusura - vedere i commenti sotto

La p è nulla perché la chiusura lo sta acquisendo prima di essere inizializzato su un riferimento. Per ottenere il comportamento che desideri, devi rendere p un valore non opzionale come var p: CLPlacemark !.

Qui di seguito è il codice che ho usato per testare la mia congettura:

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)") 

} 

Qui è log della console:

Pushit < - pulsante premuto per avviare la posizione catturare
fuori di ciò che è in p: empty
All'interno di ciò che è in p: Stati Uniti
Fuori da ciò che è in p: vuoto
All'interno di ciò che è in p: Stati Uniti
Fuori da ciò che è in p: vuoto ...

+0

. L'ho provato, tuttavia ho ottenuto EXC_BAD_ACCESS quando lo faccio. – AaronDancer

+0

L'ho provato di nuovo, sto ottenendo lo stesso risultato dell'esempio sopra quando uso un valore non opzionale. – AaronDancer

+0

Le chiusure per swift non sembrano funzionare come mi aspetterei. L'unico modo per ottenere il valore interno da "attaccare" al di fuori della chiusura è se il segnaposto fosse una variabile membro del controller. Probabilmente funzionerebbe con variabili statiche/di classe se supportate rapidamente (apparentemente non disponibili nella versione beta). Nota, ho persino provato a utilizzare un array locale inizializzato al di fuori della chiusura. Ho aggiunto (aggiunto) il segnaposto alla matrice nella chiusura. Ma al di fuori della chiusura la matrice era ancora impiegata. – JohnInSea

15

Con queste linee di Swift, è possibile stampare in modo completo l'indirizzo del luogo:

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!

-2

Le tue cose non funzionano per una serie di motivi. Ecco la parte che ho fissato senza realmente guardare la funzionalità:

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 
} 
+0

Questo restituirà ancora zero - non ti stai rendendo conto che p è impostato dopo che hai restituito il valore, dato che reverseGeocodeLocation funziona async – SomaMan

0

po 'tardi per questo partito, ma sembra che tu abbia bisogno (ndr) per fare un po' terra-up lettura di cose asincrona. Detto questo, probabilmente lo hai già imparato.

Il problema di base con il tuo codice è che p (il tuo segnaposto) viene impostato dopo il ritorno della funzione, quindi è appena perso - non è possibile utilizzare una funzione per restituire un valore asincrono. Con una chiusura di completamento, il codice viene passato al segnaposto quando arriva (in modo asincrono) allo & la chiusura viene invocata - nota che la funzione non restituisce nulla.

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) 
    }) 
} 

Usa -

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

Non ho in realtà mettere questo in Xcode, ma sembra proprio ...