Met de introductie van iOS 5 zijn honderden nieuwe API's beschikbaar voor iOS-ontwikkelaars. Een van de minder bekend gemaakte functies was de toevoeging van een geocoderings-API als onderdeel van het Core Location-framework. De klasse die de geocoderingsaanvragen afgehandeld is CLGeocoder
. In de komende twintig minuten zal ik je laten zien hoe je een applicatie kunt bouwen die een fysiek adres converteert naar een paar coördinaten met behulp van CLGeocoder
.
Wat is geocodering? Geocodering is een mooi woord voor het associëren van een paar coördinaten met een fysiek adres (reverse geocoding) en vice versa (forward geocoding). Hoewel MapKit de mogelijkheid heeft gehad om geocode-coördinaten om te keren MKReverseGeocoder
sinds de release van iOS 3, MKReverseGeocoder
is verouderd vanaf iOS 5. CLGeocoder
verwerkt geocodering in iOS 5 en het doet het op een gemakkelijke en elegante manier. Zoals de naam al aangeeft, CLGeocoder
maakt deel uit van het krachtige Core Location-framework. Naast omgekeerde geocodering, CLGeocoder
kan ook fysieke adressen vertalen naar locaties (forward geocoding) en dat is precies wat we in deze tutorial zullen doen.
We zullen een applicatie bouwen waarmee de gebruiker een straat, stad en land kan invoeren, en onze applicatie retourneert een breedtegraad en lengtegraad voor het adres, evenals een mogelijke naam van de locatie, een zogenaamd interessegebied.
Hoe werkt het CLGeocoder
doe dit? Het Core Location-raamwerk maakt verbinding met een webservice achter de schermen, maar u, als ontwikkelaar, hoeft niet te maken te krijgen met de gedetailleerde details. CLGeocoder
is daarom heel gemakkelijk te gebruiken.
Start Xcode op en maak een nieuw project door de. Te kiezen Toepassing enkele weergave sjabloon. Geef uw applicatie de naam Geocodering, voer een bedrijfsidentificatie in, selecteer iPhone voor de apparaatfamilie en controleer deze Gebruik automatische referentietelling. Je kunt de Class Prefix veld leeg en de resterende selectievakjes niet aangevinkt. Kies een locatie om uw project op te slaan en te slaan creëren.
We beginnen met het maken van de gebruikersinterface van onze applicatie. Voordat we het XIB-bestand van onze view controller openen, moeten we echter zes outlets en één actie maken. Selecteer het header-bestand van uw view controller en declareer de outlets en actie zoals getoond in het onderstaande fragment.
#importeren@interface ViewController: UIViewController __weak UITextField * _streetField; __weak UITextField * _cityField; __weak UITextField * _countryField; __zwakke UIButton * _fetchCoordinatesButton; __ zwak UILabel * _nameLabel; __ zwak UILabel * _coordinatesLabel; @property (nonatomic, weak) IBOutlet UITextField * streetField; @property (nonatomic, weak) IBOutlet UITextField * cityField; @property (nonatomic, weak) IBOutlet UITextField * countryField; @property (nonatomic, weak) IBOutlet UIButton * fetchCoordinatesButton; @property (nonatomic, weak) IBOutlet UILabel * nameLabel; @property (nonatomic, weak) IBOutlet UILabel * coordinatesLabel; - (IBAction) fetchCoordinates: (id) afzender; @einde
De eerste drie verkooppunten zijn voorbeelden van UITextField
waarin de gebruiker een straat, stad en land kan invoeren. De vierde uitlaat is een instantie van UIButton
die onze actie activeert wanneer de gebruiker erop tikt. De laatste twee verkooppunten zijn voorbeelden van UILabel
die we zullen gebruiken om de resultaten van ons geocoderingsverzoek weer te geven. Als ons geocoderingsverzoek met succes terugkeert, zullen we de naam van de locatie in het eerste label weergeven (later meer hierover) en de coördinaten van de locatie in het tweede label. Maak je geen zorgen als dit je verwart. Het zal logischer zijn als we alles in ons xib-bestand inpluggen.
We verklaren ook één methode die (1) zal schieten en (2) onze geocoderingsaanvraag zal behandelen. Deze actie wordt gekoppeld aan onze knop. Vraag je je af waarom we een stopcontact nodig hebben voor onze knop? Ik zal je daar aan het einde van deze tutorial meer over vertellen.
Vergeet niet om accessors voor de outlets te synthetiseren. U moet ook een lege implementatie van onze actie maken om compilerwaarschuwingen te voorkomen.
@synthesize streetField = _streetField, cityField = _cityField, countryField = _countryField, fetchCoordinatesButton = _fetchCoordinatesButton, nameLabel = _nameLabel, coordinatesLabel = _coordinatesLabel; - (IBAction) fetchCoordinates: (id) afzender NSLog (@ "Ophaalcoördinaten");
Klaar? Ga naar het xib-bestand van onze view-controller en sleep drie tekstvelden, één knop en twee labels naar de view van je view-controller. Plaats ze zoals ik heb in de onderstaande afbeelding en geef de knop een titel van Coördinaten ophalen om de gebruiker te laten weten wat er zal gebeuren wanneer op de knop wordt getikt.
Zorg ervoor dat u een tijdelijke aanduiding aan elk tekstveld toevoegt om de gebruiker te laten weten welk type informatie elk tekstveld verwacht. Ik heb de labels ook zo geconfigureerd dat witte tekst op een blauwe achtergrond staat om het op te laten vallen.
Laten we eens kijken wat we tot nu toe hebben. De gebruiker kan een straat en een nummer invoeren in het eerste tekstveld, een stad in het tweede tekstveld en een land in het derde tekstveld. Wanneer de gebruiker op de Coördinaten ophalen knop, zal onze applicatie een geocoderingsaanvraag doen voor het adres dat de gebruiker heeft ingevoerd. Als ons verzoek succesvol is, geven we de naam van de locatie en de coördinaten (breedte- en lengtegraad) in de labels weer.
Met de gebruikersinterface op zijn plaats, zijn we klaar om onze stopcontacten en actie aan te sluiten. Druk voor de uitgangen op de navigatietoets en sleep vanuit de Bestand eigenaar naar de tekstvelden en kies de juiste IBOutlet
uit het menu dat verschijnt. Doe hetzelfde voor de knop en labels. Voor de actie drukt u nogmaals op de navigatietoets en sleept u van onze knop naar de Bestand eigenaar en kies de fetchCoordinates: methode uit het menu dat verschijnt. Dit zal onze actie verbinden met de knoppen UIControlEventTouchUpInside
evenement en dat is precies wat we willen.
Voordat we beginnen met de implementatie van fetchCoordinates: methode, we moeten het toevoegen Kernlocatie kader voor ons project. Selecteer ons project in de Project Navigator en kies het enige doel in de doelenlijst. Kies bovenaan de Bouw fases tab en open de Binaire koppeling met bibliotheken lade. Druk op het plusteken en kies Kernlocatie van de lijst die appeaers. Ons project is nu gekoppeld aan het Core Location-raamwerk.
Er is nog een laatste ding dat we moeten doen voordat we gebruik kunnen maken van het Core Location-framework. Navigeer terug naar het header-bestand van onze view-controller en voeg een nieuwe import-instructie toe onder de UIKit-importinstructie.
#importeren#importeren
De import-opdracht importeert de framework-headers van de Core Location en zorgt ervoor dat we de functionaliteit ervan kunnen gebruiken in onze view-controller. We moeten ook een instantievariabele maken voor het geocoder-object die we gebruiken voor het maken van geocoderingsaanvragen. Zoals ik aan het begin van deze tutorial al zei, gebruiken we een instantie van CLGeocoder
Voor dit doeleinde. Voeg een instantievariabele en een eigenschap toe aan het headerbestand van uw view-controller en vergeet niet om de accessors te synthetiseren in het implementatiebestand van uw view-controller. We zijn nu klaar om wat magie te doen.
#importeren#importeren @interface ViewController: UIViewController CLGeocoder * _geocoder; __weak UITextField * _streetField; __weak UITextField * _cityField; __weak UITextField * _countryField; __zwakke UIButton * _fetchCoordinatesButton; __ zwak UILabel * _nameLabel; __ zwak UILabel * _coordinatesLabel; @property (nonatomic, strong) CLGeocoder * geocoder; @property (nonatomic, weak) IBOutlet UITextField * streetField; @property (nonatomic, weak) IBOutlet UITextField * cityField; @property (nonatomic, weak) IBOutlet UITextField * countryField; @property (nonatomic, weak) IBOutlet UIButton * fetchCoordinatesButton; @property (nonatomic, weak) IBOutlet UILabel * nameLabel; @property (nonatomic, weak) IBOutlet UILabel * coordinatesLabel; - (IBAction) fetchCoordinates: (id) afzender; @einde
// Vergeet niet om de geocoder te synthetiseren: @synthesize geocoder = _geocoder; - (IBAction) fetchCoordinates: (id) afzender NSLog (@ "Ophaalcoördinaten");
Ik ga door de fetchCoordinates: methode stap voor stap. We controleren eerst of onze geocoder-instantie is ingesteld. Als dit niet het geval is, initialiseren we het. Het is vaak een goede gewoonte om een object alleen te initialiseren wanneer u het echt nodig hebt.
if (! self.geocoder) self.geocoder = [[CLGeocoder alloc] init];
In dit voorbeeld doen we een verzoek voor een geocodering voorwaarts, wat betekent dat we een adres verzenden naar de webservice waar Core Location naar toe spreekt en dat het locatiegegevens naar ons terugstuurt. De methode die we zullen gebruiken, accepteert een adresstring, wat betekent dat we de adresgegevens uit onze tekstvelden moeten samenvoegen.
NSString * -adres = [NSString stringWithFormat: @ "% @% @% @", self.streetField.text, self.cityField.text, self.countryField.text];
Eindelijk bellen we geocodeAddressString: completionHandler: op ons geocoder-object. Deze methode accepteert twee argumenten: (1) onze adresreeks en (2) een voltooiingsblok. Dit is weer een nette toepassing van blokken die de kracht demonstreert die ze gebruiken.
[self.geocoder geocodeAddressString: address completionHandler: ^ (NSArray * plaatsmarkeringen, NSError * -fout) if ([plaatsmarkeringen tellen mee]> 0) CLPlacemark * plaatsmarkering = [plaatsmarkeringen objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; if ([placemark.areasOfInterest count]> 0) NSString * areaOfInterest = [markeringspunt.area'sOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest; else self.nameLabel.text = @ "Er is geen interessegebied gevonden"; ];
Het voltooiingsblok neemt twee argumenten, (1) een reeks locaties (zogenaamde plaatsmarkeringen) en (2) een fout in het geval er iets misgaat. Waarom krijgen we een reeks locaties in plaats van slechts één locatie? Wanneer het adres dat we naar de webservice sturen niet specifiek genoeg is, is het mogelijk dat de webservice niet één, maar verschillende plaatsmarkeringen retourneert die overeenkomen met het adres. Verschillende wat? Een plaatsmarkering, een instantie van CLPlacemark
, is een container voor gegevens die zijn gekoppeld aan een paar coördinaten. Het bevat meer dan alleen de coördinaten, zoals de straat, stad en land, en ook interessegebieden, zoals gebouwen, nationale parken en historische monumenten..
Voor ons project willen we alleen de coördinaat van de plaatsmarkering en het interessegebied als er een is. Ik raad u aan de hele reeks plaatsmarkeringen bij de console te registreren om te zien wat deze bevat. Dit is altijd een goede manier om nieuwe API's te verkennen.
In ons voltooiingsblok controleren we eerst of onze reeks plaatsmarkeringen objecten bevat, met andere woorden, heeft de webservice een locatie aan ons adres kunnen koppelen. Als we een niet-lege array hebben, pakken we het eerste object. Natuurlijk wilt u in een echte toepassing een aantal foutcontroles uitvoeren om te controleren of u een plaatsmarkering heeft gevonden die voor u van belang is en om ervoor te zorgen dat de fout van het voltooiingsblok nul is.
Een van de eigenschappen van een exemplaar van CLPlacemark is de locatie, wat een CLLocation
voorwerp. Als u niet bekend bent met CLLocation
objecten, ze bevatten de coördinaat (CLLocationCoordinate2D
) waarnaar we op zoek zijn, maar ook een maat voor de nauwkeurigheid van de locatie. CLLocation
objecten worden overal in het Core Location-framework gebruikt en zijn ongelooflijk nuttig.
Om het resultaat van ons verzoek op het scherm weer te geven, nemen we de geografische lengte- en breedtegraad van de locatie-eigenschap van het plaatsmarkering en geven deze weer in ons label. We controleren ook of de reeks interessegebieden van het markeringspunt niet leeg is. Als dat zo is, pakken we het eerste object dat het bevat (een instantie van NSString
) en geef dit weer in ons eerste label. Als er geen interessegebieden zijn gevonden, vertellen we de gebruiker door een eenvoudig bericht weer te geven.
Ik wil nog een laatste tip toevoegen aan onze applicatie om de gebruikerservaring te verbeteren en ook om de richtlijnen van Apple te volgen. Wanneer we een geocoderingsverzoek indienen, ontvangen we niet onmiddellijk een antwoord. Zoals ik eerder al zei, praat het Core Location-framework met een webservice en ontvangt een antwoord. Wanneer het antwoord binnenkomt, hangt af van verschillende factoren, zoals de snelheid van onze netwerkverbinding. De documentatie van CLGeocoder
stelt dat verzoeken aan de webseriv met mate moeten worden gedaan. Met andere woorden, de gebruiker (1) zou in een kort tijdsinterval niet meerdere verzoeken moeten doen door meerdere keren op de knop te tikken en (2) de gebruiker zou geen verzoek kunnen doen voordat het actieve verzoek een antwoord heeft geretourneerd. Om dit laatste te bereiken, schakelen we de knop uit totdat het verzoek is voltooid (succesvol of niet succesvol). Om dit te doen, schakelen we de knop uit voordat we het verzoek doen en schakelen we de knop opnieuw in het voltooiingsblok in. Bekijk de volledige implementatie van onze fetchCoordinates: methode voor verduidelijking. Voer uw toepassing uit en voer een adres in om het uit te proberen.
- (IBAction) fetchCoordinates: (id) afzender if (! Self.geocoder) self.geocoder = [[CLGeocoder alloc] init]; NSString * adres = [NSString stringWithFormat: @ "% @% @% @", self.streetField.text, self.cityField.text, self.countryField.text]; self.fetchCoordinatesButton.enabled = NO; [self.geocoder geocodeAddressString: address completionHandler: ^ (NSArray * plaatsmarkeringen, NSError * -fout) if ([plaatsmarkeringen tellen mee]> 0) CLPlacemark * plaatsmarkering = [plaatsmarkeringen objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; if ([placemark.areasOfInterest count]> 0) NSString * areaOfInterest = [markeringspunt.area'sOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest; self.fetchCoordinatesButton.enabled = YES; ];
We kunnen een stap verder gaan door tijdens het verzoek een activiteitsindicator weer te geven en de knop te verbergen, maar ik laat dat aan u over als een uitdaging. Ik heb deze functie toegevoegd aan de broncode bij deze zelfstudie.
Kernlocatie is een zeer krachtig kader geworden en CLGeocoder
is slechts een van de vele klassen die je helpen om een complexe taak te volbrengen met gemak en heel weinig overhead. Genieten!