Forward Geocoding med CLGeocoder

Med introduksjonen av iOS 5 har hundrevis av nye API-er blitt tilgjengelig for iOS-utviklere. En av de mindre publiserte funksjonene var tillegget til en geokodings-API som en del av Core Location-rammen. Klassen som håndterer forespørsler om geokoding er CLGeocoder. I de neste tjue minuttene vil jeg vise deg hvordan du bygger et program som konverterer en fysisk adresse til et par koordinater ved å bruke CLGeocoder.

Hva er geokoding? Geokoding er et fancy ord for å knytte et par koordinater med en fysisk adresse (omvendt geokoding) og omvendt (fremad geokoding). Selv om MapKit har hatt muligheten til å reversere geokodekoordinater med MKReverseGeocoder siden utgivelsen av iOS 3, MKReverseGeocoder har blitt avskrevet som av iOS 5. CLGeocoder håndterer geokoding i iOS 5, og det gjør det på en enkel og elegant måte. Som navnet antyder, CLGeocoder er en del av det kraftige Core Location-rammeverket. I tillegg til omvendt geokoding, CLGeocoder kan også oversette fysiske adresser til steder (fremover geokoding) og det er akkurat det vi skal gjøre i denne opplæringen.

Vi vil bygge et program som lar brukeren angi en gate, by og land, og vår søknad vil returnere bredde og lengdegrad for adressen, samt et mulig navn på stedet, et såkalt område av interesse.

Hvordan gjør CLGeocoder gjør dette? Kjerneplasseringsrammen knytter seg til en webtjeneste bak kulissene, men du, som utvikler, trenger ikke å håndtere de nitty gritty detaljene. CLGeocoder er derfor veldig enkelt å bruke.


Trinn 1: Prosjektoppsett

Brann opp Xcode og opprett et nytt prosjekt ved å velge Enkeltvisningsprogram mal. Navngi søknaden Geocoding, skriv inn et firmanavn, velg iPhone for enhetsfamilien, og sørg for å sjekke Bruk automatisk referansetelling. Du kan forlate Klasse prefiks feltet tomt og de resterende avmerkingsboksene er ikke merket. Velg et sted for å lagre prosjektet ditt og klikk Skape.


Trinn 2: Opprette brukergrensesnittet

Vi starter med å skape brukergrensesnittet i applikasjonen vår. Før vi åpner visningscontrollerens XIB-fil, må vi imidlertid opprette seks uttak og en handling. Velg tittelkontrollens headerfil og erklær om uttak og handling som vist i utdragsboken nedenfor.

 #importere  @interface ViewController: UIViewController __weak UITextField * _streetField; __weak UITextField * _cityField; __weak UITextField * _countryField; __weak UIButton * _fetchCoordinatesButton; __weak UILabel * _nameLabel; __weak UILabel * _coordinatesLabel;  @property (ikkeatomisk, svak) IBOutlet UITextField * streetField; @property (ikkeatomisk, svak) IBOutlet UITextField * cityField; @property (ikkeatomisk, svak) IBOutlet UITextField * countryField; @property (ikkeatomisk, svak) IBOutlet UIButton * fetchCoordinatesButton; @property (ikkeatomisk, svak) IBOutlet UILabel * nameLabel; @property (nonatomic, weak) IBOutlet UILabel * koordinaterLabel; - (IBAction) fetchCoordinates: (id) avsender; @slutt

De tre første utsalgssteder er forekomster av UITextField der brukeren kan gå inn i en gate, by og land. Det fjerde uttaket er en forekomst av UIButton Det vil utløse vår handling når brukeren tapper den. De siste to utsalgssteder er forekomster av UILabel som vi vil bruke til å vise resultatene av vår geokodingsforespørsel. Hvis vår forespørsel om geokoding returneres, viser vi navnet på stedet i den første etiketten (mer om dette senere) og koordinatene til stedet i den andre etiketten. Ikke bekymre deg hvis dette forvirrer deg. Det blir mer fornuftig når vi kobler alt opp i vår xib-fil.

Vi erklærer også en metode som vil (1) brann og (2) håndtere vår geokodingforespørsel. Denne handlingen vil bli koblet til vår knapp. Lurer du på hvorfor vi trenger et uttak for knappen vår? Jeg vil fortelle deg mer om det på slutten av denne opplæringen.

Ikke glem å syntetisere accessors for uttak. Du bør også opprette en tom implementering av vår handling for å unngå kompilervarsler.

 @synthesize streetField = _streetField, cityField = _cityField, countryField = _countryField, fetchCoordinatesButton = _fetchCoordinatesButton, nameLabel = _nameLabel, koordinaterLabel = _coordinatesLabel; - (IBAction) fetchCoordinates: (id) avsender NSLog (@ "Hent koordinater"); 

Klar? Gå over til vår visningskontrollers xib-fil og dra tre tekstfelter, en knapp og to etiketter til visningskontrollørens visning. Plasser dem som jeg har i figuren under og gi knappen en tittel på Hent koordinater for å la brukeren vite hva som vil skje når knappen er tappet.

Sørg for å legge til en plassholder i hvert tekstfelt for å la brukeren få vite hva slags informasjon hvert tekstfelt forventer. Jeg har også konfigurert etikettene til å ha hvit tekst på en blå bakgrunn for å la den skille seg ut.

La oss se hva vi har så langt. Brukeren kan legge inn en gate og et nummer i det første tekstfeltet, en by i det andre tekstfeltet og et land i det tredje tekstfeltet. Når brukeren tapper på Hent koordinater knappen, vil vår søknad gi en geokodingsforespørsel for adressen som brukeren har angitt. Hvis vår forespørsel er vellykket, viser vi navnet på plasseringen og koordinatene (breddegrad og lengdegrad) i etikettene.

Med brukergrensesnittet på plass, er vi klare til å koble opp våre utsalgssteder og tiltak. For uttakene trykker du på kontrolltasten og drar fra Filens eier til tekstfeltene og velg det aktuelle IBOutlet fra menyen som dukker opp. Gjør det samme for knappen og etikettene. For handlingen, trykk på kontrolltasten en gang til, og dra fra knappen vår til Filens eier og velg fetchCoordinates: metode fra menyen som dukker opp. Dette vil koble vår handling til knappens UIControlEventTouchUpInside arrangement og det er akkurat det vi vil ha.


Trinn 3: Legge til kjerneposisjon til blandingen

Før vi begynner å implementere fetchCoordinates: metode, må vi legge til Kjerneplassering rammeverk for vårt prosjekt. Velg vårt prosjekt i Prosjektnavigator og velg det eneste målet i mållisten. På toppen, velg Bygg faser kategorien og åpne Link binær med biblioteker skuff. Treff plustegnet og velg Kjerneplassering fra listen som appeaers. Prosjektet vårt er nå koblet til Core Location-rammen.

Det er en siste ting vi må gjøre før vi kan gjøre bruk av Core Location-rammeverket. Naviger tilbake til tittelkontrollens headerfil og legg til en ny importoppgave under UIKit-importoppstillingen.

 #importere  #importere 

Importoppgaven importerer rammeoverskriftene for Core Location og sikrer at vi kan bruke funksjonaliteten i vår visningskontroller. Vi må også opprette en instansvariabel for geokoderobjektet som vi skal bruke for å lage geokodingsforespørsler. Som jeg nevnte i begynnelsen av denne opplæringen, bruker vi en forekomst av CLGeocoder for dette formålet. Legg til en instansvariabel og en egenskap i headerfilen til visningskontrollen din, og ikke glem å syntetisere sine accessorer i visningskontrollens implementasjonsfil. Vi er nå klar til å gjøre noe magi.

 #importere  #importere  @interface ViewController: UIViewController CLGeocoder * _geocoder; __weak UITextField * _streetField; __weak UITextField * _cityField; __weak UITextField * _countryField; __weak UIButton * _fetchCoordinatesButton; __weak UILabel * _nameLabel; __weak UILabel * _coordinatesLabel;  @property (nonatomic, strong) CLGeocoder * geocoder; @property (ikkeatomisk, svak) IBOutlet UITextField * streetField; @property (ikkeatomisk, svak) IBOutlet UITextField * cityField; @property (ikkeatomisk, svak) IBOutlet UITextField * countryField; @property (ikkeatomisk, svak) IBOutlet UIButton * fetchCoordinatesButton; @property (ikkeatomisk, svak) IBOutlet UILabel * nameLabel; @property (nonatomic, weak) IBOutlet UILabel * koordinaterLabel; - (IBAction) fetchCoordinates: (id) avsender; @slutt
 // Husk å syntetisere geokoden: @synthesize geocoder = _geocoder; - (IBAction) fetchCoordinates: (id) avsender NSLog (@ "Hent koordinater"); 

Trinn 4: Forward Geocoding

Jeg vil gå gjennom fetchCoordinates: metode trinn for trinn. Vi kontrollerer først om vår forekomst av geokoder er satt. Hvis dette ikke er tilfelle, initialiserer vi det. Det er ofte god praksis å bare initialisere et objekt når du faktisk trenger det.

 hvis (! self.geocoder) self.geocoder = [[CLGeocoder alloker] init]; 

I dette eksemplet vil vi foreta en forutgående geokodingforespørsel, noe som betyr at vi sender en adresse til webtjenesten som Core Location snakker om, og det vil sende stedsdata tilbake til oss. Metoden som vi skal bruke aksepterer en adressestreng, noe som betyr at vi må sammenkalle adressedata fra våre tekstfelter.

 NSString * adresse = [NSString stringWithFormat: @ "% @% @% @", self.streetField.text, self.cityField.text, self.countryField.text];

Til slutt, vi ringer geocodeAddressString: completionHandler: på vårt geokoderobjekt. Denne metoden aksepterer to argumenter: (1) vår adressestreng og (2) en ferdigstillingsblokk. Dette er en annen fin bruk av blokker som demonstrerer kraften de har.

 [self.geocoder geocodeAddressString: adresse completionHandler: ^ (NSArray * stedsmarkeringer, NSError * feil) if ([stedsmarkeringer teller]> 0) CLPlacemark * placemark = [stedsmarkeringer objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; hvis ([placemark.areasOfInterest count]> 0) NSString * areaOfInterest = [placemark.areasOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest;  else self.nameLabel.text = @ "Ingen interesseområde ble funnet"; ];

Fullføringsblokken tar to argumenter, (1) en rekke steder (såkalte stedsmarkeringer) og (2) en feil hvis noe går galt. Hvorfor får vi en rekke steder i stedet for bare ett sted? Når adressen vi sender til webtjenesten ikke er spesifikk nok, er det mulig at webtjenesten ikke returnerer en, men flere stedsmarkeringer som samsvarer med adressen. Flere hva? Et stedsmerke, en forekomst av CLPlacemark, er en beholder for data knyttet til et par koordinater. Den inneholder mer enn bare koordinatene, for eksempel gaten, byen og landet, og også områder av interesse, for eksempel bygninger, nasjonalparker og historiske monumenter.

For vårt prosjekt ønsker vi bare koordinaten til stedsmarkedet og interessepunktet hvis det er en. Jeg oppfordrer deg til å logge hele spekteret av stedsmarkeringer til konsollen for å se hva den inneholder. Dette er alltid en god måte å utforske nye API-er.

I vår ferdigstillingsblokk kontrollerer vi først om vårt utvalg av stedsmarkeringer inneholder noen objekter, med andre ord, har webtjenesten vært i stand til å knytte et sted med adressen vår. Hvis vi har en ikke-tom rekkefølge, tar vi det første objektet. Selvfølgelig, i en ekte applikasjon vil du kanskje gjøre noen feilkontroll for å sikre at du har funnet en stedsmarkør som er av interesse for deg, og også at feilen i ferdigstillingsblokken er null.

En av egenskapene til et CLPlacemark-forekomst er beliggenheten, som er en CLLocation gjenstand. Hvis du ikke er kjent med CLLocation objekter, de inneholder koordinaten (CLLocationCoordinate2D) som vi leter etter, men også et mål for nøyaktigheten av lokasjonen. CLLocation Objekter brukes i hele Core Location-rammen og er utrolig nyttig.

For å vise resultatet av vår forespørsel på skjermen, tar vi tak i breddegrad og lengdegrad for stedsmarkørens plasseringsegenskap og viser den i vår etikett. Vi kontrollerer også om stedsmarkørens rekke interessepunkter ikke er tomt. Hvis det er, tar vi det første objektet den inneholder (en forekomst av NSString) og vise den i vår første etikett. Hvis ingen interesseområder ble funnet, forteller vi brukeren ved å vise en enkel melding.

Jeg vil legge til en siste berøring til applikasjonen vår for å forbedre brukeropplevelsen og også følge Apples retningslinjer. Når vi foretar en geokodningsforespørsel, får vi ikke umiddelbar respons. Som nevnt tidligere, snakker Core Location-rammen til en webtjeneste og mottar et svar. Når svaret kommer, avhenger av ulike faktorer, for eksempel hastigheten på nettverksforbindelsen. Dokumentasjonen av CLGeocoder sier at forespørsler til webserivce skal gjøres sparsomt. Med andre ord, brukeren (1) ikke skal gjøre flere forespørsler på kort tid, ved å trykke på knappen flere ganger og (2) brukeren ikke bør kunne gjøre en forespørsel før den aktive forespørselen har returnert et svar. For å oppnå sistnevnte, deaktiverer vi knappen til forespørselen er ferdig (vellykket eller mislykket). For å gjøre dette, deaktiverer vi knappen før vi gjør forespørselen og aktiverer knappen igjen i ferdigstillingsblokken. Ta en titt på hele implementeringen av vår fetchCoordinates: metode for avklaring. Kjør din søknad og skriv inn en adresse for å sette den gjennom sine skritt.

 - (IBAction) fetchCoordinates: (id) sender if (! Self.geocoder) self.geocoder = [[CLGeocoder alloker] init];  NSString * adresse = [NSString stringWithFormat: @ "% @% @% @", self.streetField.text, self.cityField.text, self.countryField.text]; self.fetchCoordinatesButton.enabled = NO; [self.geocoder geocodeAddressString: adresse completionHandler: ^ (NSArray * stedsmarkeringer, NSError * feil) if ([stedsmarkeringer teller]> 0) CLPlacemark * placemark = [stedsmarkeringer objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; hvis ([placemark.areasOfInterest count]> 0) NSString * areaOfInterest = [placemark.areasOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest;  self.fetchCoordinatesButton.enabled = YES; ]; 

Vi kunne ta det et skritt videre ved å vise en aktivitetsindikator under forespørselen og gjemme knappen, men jeg la det opp til deg som en utfordring. Jeg har lagt til denne funksjonen til kildekoden som følger med denne opplæringen.


Konklusjon

Core Location har blitt et veldig kraftig rammeverk og CLGeocoder er bare en av de mange klassene som hjelper deg med å oppnå en kompleks oppgave med letthet og svært lite overhode. Nyt!