Géocodage en avant avec CLGeocoder

Avec l'introduction d'iOS 5, des centaines de nouvelles API sont désormais disponibles pour les développeurs iOS. L'une des fonctionnalités les moins connues a été l'ajout d'une API de géocodage dans le cadre de la structure Core Location. La classe traitant les requêtes de géocodage est CLGeocoder. Dans les vingt prochaines minutes, je vais vous montrer comment construire une application qui convertit une adresse physique en une paire de coordonnées en utilisant CLGeocoder.

Qu'est-ce que le géocodage? Géocodage est un mot de fantaisie pour associer une paire de coordonnées à une adresse physique (géocodage inverse) et inversement (géocodage direct). Même si MapKit a eu la capacité d’inverser les coordonnées du géocodage avec MKReverseGeocoder depuis la sortie d'iOS 3, MKReverseGeocoder est obsolète depuis iOS 5. CLGeocoder gère le géocodage dans iOS 5 et le fait de manière simple et élégante. Comme son nom l'indique, CLGeocoder fait partie du puissant cadre Core Location. En plus du géocodage inversé, CLGeocoder peut également traduire des adresses physiques en emplacements (géocodage en aval) et c'est exactement ce que nous allons faire dans ce tutoriel.

Nous allons créer une application qui permet à l'utilisateur de saisir une rue, une ville et un pays, et notre application renverra une latitude et une longitude pour l'adresse, ainsi qu'un nom possible pour l'emplacement, appelé zone d'intérêt..

Comment CLGeocoder fais ça? L'infrastructure Core Location se connecte à un service Web dans les coulisses, mais vous, en tant que développeur, n'avez pas à traiter avec les détails les plus importants.. CLGeocoder est donc très facile à utiliser.


Étape 1: Configuration du projet

Lancez Xcode et créez un nouveau projet en choisissant le Application à vue unique modèle. Nommez votre application Géocodage, entrez un identifiant d'entreprise, sélectionnez iPhone pour la famille d'appareils et assurez-vous de vérifier Utiliser le comptage automatique des références. Vous pouvez laisser le Préfixe de classe champ vide et les cases à cocher restantes non cochées. Choisissez un emplacement pour enregistrer votre projet et appuyez sur Créer.


Étape 2: Création de l'interface utilisateur

Nous commençons par créer l'interface utilisateur de notre application. Avant d'ouvrir le fichier XIB de notre contrôleur de vue, nous devons toutefois créer six prises et une action. Sélectionnez le fichier d'en-tête de votre contrôleur de vue et déclarez les prises et l'action comme indiqué dans l'extrait de code ci-dessous..

 #importation  @interface ViewController: UIViewController __weak UITextField * _streetField; __weak UITextField * _cityField; __weak UITextField * _countryField; __weak UIButton * _fetchCoordinatesButton; __weak UILabel * _nameLabel; __weak UILabel * _coordinatesLabel;  @property (nonatomic, faible) IBOutlet UITextField * streetField; @property (nonatomic, faible) IBOutlet UITextField * cityField; @property (nonatomic, faible) IBOutlet UITextField * countryField; @property (nonatomic, faible) IBOutlet UIButton * fetchCoordinatesButton; @property (nonatomic, faible) IBOutlet UILabel * nameLabel; @property (nonatomic, faible) IBOutlet UILabel * coordinalsLabel; - (IBAction) fetchCoordinates: (id) expéditeur; @fin

Les trois premiers points de vente sont des exemples de UITextField dans lequel l'utilisateur peut entrer une rue, une ville et un pays. Le quatrième point de vente est une instance de UIButton cela déclenchera notre action lorsque l'utilisateur le tapera. Les deux derniers points de vente sont des exemples de UILabel que nous utiliserons pour afficher les résultats de notre demande de géocodage. Si notre demande de géocodage retourne avec succès, nous afficherons le nom de l'emplacement dans la première étiquette (plus d'informations à ce sujet ultérieurement) et les coordonnées de l'emplacement dans la deuxième étiquette. Ne vous inquiétez pas si cela vous déroute. Cela aura plus de sens une fois que nous aurons tout connecté dans notre fichier xib.

Nous déclarons également une méthode qui (1) déclenche et (2) traite notre demande de géocodage. Cette action sera reliée à notre bouton. Vous vous demandez pourquoi nous avons besoin d'un point de vente pour notre bouton? Je vous en dirai plus à la fin de ce tutoriel..

N'oubliez pas de synthétiser des accesseurs pour les prises. Vous devez également créer une implémentation vide de notre action pour éviter les avertissements du compilateur..

 @synthesize streetField = _streetField, cityField = _cityField, countryField = _countryField, fetchCoordinatesButton = _fetchCoordinatesButton, nameLabel = _nameLabel, CoordinatesLabel = _coordinatesLabel; - (IBAction) fetchCoordinates: (id) expéditeur NSLog (@ "Extraction de coordonnées"); 

Prêt? Rendez-vous sur le fichier xib de notre contrôleur de vue et faites glisser trois champs de texte, un bouton et deux étiquettes vers la vue de votre contrôleur de vue. Positionnez-les comme dans la figure ci-dessous et donnez au bouton le titre de Chercher les coordonnées pour permettre à l'utilisateur de savoir ce qui se passera lorsque vous appuierez sur le bouton.

Veillez à ajouter un espace réservé à chaque champ de texte pour permettre à l'utilisateur de connaître le type d'informations attendu par chaque champ de texte. J'ai également configuré les étiquettes pour avoir du texte blanc sur un fond bleu pour le laisser ressortir.

Voyons ce que nous avons jusqu'à présent. L'utilisateur peut entrer une rue et un numéro dans le premier champ de texte, une ville dans le deuxième champ de texte et un pays dans le troisième champ de texte. Lorsque l'utilisateur appuie sur le Chercher les coordonnées bouton, notre application fera une demande de géocodage pour l'adresse que l'utilisateur a entrée. Si notre demande aboutit, nous affichons le nom de la localisation et les coordonnées (latitude et longitude) dans les libellés..

Avec l'interface utilisateur en place, nous sommes prêts à connecter nos points de vente et nos actions. Pour les prises, appuyez sur la touche de navigation et faites-la glisser de la Propriétaire du fichier aux champs de texte et choisissez le approprié IBOutlet dans le menu qui apparaît. Faites la même chose pour le bouton et les étiquettes. Pour l’action, appuyez une fois de plus sur la touche de contrôle et faites glisser de notre bouton vers le Propriétaire du fichier et choisissez le fetchCoordinates: méthode du menu qui apparaît. Cela connectera notre action au bouton UIControlEventTouchUpInside événement et c'est exactement ce que nous voulons.


Étape 3: Ajout de l'emplacement principal au mélange

Avant de commencer à mettre en œuvre le fetchCoordinates: méthode, nous devons ajouter le Emplacement de base cadre à notre projet. Sélectionnez notre projet dans le Navigateur de projet et choisissez la seule cible dans la liste des cibles. Au sommet, choisissez le Phases de construction onglet et ouvrez le Lien binaire avec des bibliothèques tiroir. Appuyez sur le signe plus et choisissez Emplacement de base de la liste qui apparaît. Notre projet est maintenant lié au cadre Core Location.

Il nous reste une dernière chose à faire avant de pouvoir utiliser le cadre Core Location. Revenez au fichier d'en-tête de notre contrôleur de vue et ajoutez une nouvelle instruction d'importation en dessous de l'instruction d'importation UIKit..

 #importation  #importation 

L'instruction d'importation importe les en-têtes du cadre Core Location et garantit que nous pouvons utiliser ses fonctionnalités dans notre contrôleur de vue. Nous devons également créer une variable d'instance pour l'objet géocodeur que nous utiliserons pour effectuer des requêtes de géocodage. Comme je l’ai mentionné au début de ce didacticiel, nous utiliserons une instance de CLGeocoder dans ce but. Ajoutez une variable d'instance et une propriété au fichier d'en-tête de votre contrôleur de vue et n'oubliez pas de synthétiser ses accesseurs dans le fichier d'implémentation de votre contrôleur de vue. Nous sommes maintenant prêts à faire de la magie.

 #importation  #importation  @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 (nonatomic, faible) IBOutlet UITextField * streetField; @property (nonatomic, faible) IBOutlet UITextField * cityField; @property (nonatomic, faible) IBOutlet UITextField * countryField; @property (nonatomic, faible) IBOutlet UIButton * fetchCoordinatesButton; @property (nonatomic, faible) IBOutlet UILabel * nameLabel; @property (nonatomic, faible) IBOutlet UILabel * coordinalsLabel; - (IBAction) fetchCoordinates: (id) expéditeur; @fin
 // N'oubliez pas de synthétiser le géocodeur: @synthesize geocoder = _geocoder; - (IBAction) fetchCoordinates: (id) expéditeur NSLog (@ "Extraction de coordonnées"); 

Étape 4: Géocodage en aval

Je vais passer par le fetchCoordinates: méthode étape par étape. Nous vérifions d’abord si notre instance de géocodeur est définie. Si ce n'est pas le cas, nous l'initialisons. Il est souvent recommandé d'initialiser un objet uniquement lorsque vous en avez réellement besoin..

 if (! self.geocoder) self.geocoder = [[CLGeocoder alloc] init]; 

Dans cet exemple, nous ferons une demande de géocodage, ce qui signifie que nous enverrons une adresse au service Web avec lequel Core Location communiquera et qu'il nous renverra les données de localisation. La méthode que nous allons utiliser accepte une chaîne d'adresse, ce qui signifie que nous devons concaténer les données d'adresse de nos champs de texte..

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

Enfin, nous appelons geocodeAddressString: completionHandler: sur notre objet géocodeur. Cette méthode accepte deux arguments: (1) notre chaîne d’adresse et (2) un bloc d’achèvement. Ceci est une autre application soignée de blocs qui démontre la puissance qu'ils exploitent.

 [self.geocoder geocodeAddressString: address completionHandler: ^ (repères NSArray *, erreur NSError *) if ([nombre de repères]]> 0) CLPlacemark * placemark = [repères objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D Coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordonnée.latitude, coordonnée.longitude]; if ([nombre de placemark.areasOfInterest]> 0) NSString * areaOfInterest = [placemark.areasOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest;  else self.nameLabel.text = @ "Aucune zone d'intérêt n'a été trouvée"; ];

Le bloc d'achèvement prend deux arguments, (1) un tableau d'emplacements (appelés repères) et (2) une erreur en cas de problème. Pourquoi avons-nous un éventail d'emplacements au lieu d'un seul? Lorsque l'adresse que nous envoyons au service Web n'est pas assez spécifique, il est possible que le service Web ne renvoie pas un, mais plusieurs repères correspondant à l'adresse. Plusieurs quoi? Un repère, une instance de CLPlacemark, est un conteneur de données associé à une paire de coordonnées. Il contient plus que de simples coordonnées, telles que la rue, la ville et le pays, ainsi que des zones d'intérêt telles que les bâtiments, les parcs nationaux et les monuments historiques..

Pour notre projet, nous ne voulons que les coordonnées du repère et la zone d'intérêt, le cas échéant. Je vous encourage à consigner toute la gamme de repères sur la console pour voir ce qu’elle contient. C'est toujours un bon moyen d'explorer les nouvelles API.

Dans notre bloc d'achèvement, nous vérifions d'abord si notre tableau de repères contient des objets. En d'autres termes, le service Web a-t-il pu associer un emplacement à notre adresse? Si nous avons un tableau non vide, nous prenons le premier objet. Bien sûr, dans une application réelle, vous pouvez effectuer une vérification des erreurs pour vous assurer que vous avez trouvé un repère qui vous intéresse et que l'erreur du bloc d'achèvement est égale à zéro..

L’une des propriétés d’une instance de CLPlacemark est son emplacement, qui est une CLLocation objet. Si vous n'êtes pas familier avec CLLocation objets, ils contiennent la coordonnée (CLLocationCoordinate2D) que nous recherchons, mais aussi une mesure de la précision de la localisation. CLLocation les objets sont utilisés dans le cadre de Core Location et sont incroyablement utiles.

Pour afficher le résultat de notre demande à l'écran, nous saisissons la latitude et la longitude de la propriété de localisation du repère et les affichons dans notre étiquette. Nous vérifions également si le tableau des zones d'intérêt du repère est non vide. Si tel est le cas, nous saisissons le premier objet qu’il contient (une instance de NSString) et l'afficher dans notre première étiquette. Si aucun domaine d'intérêt n'a été trouvé, nous en informons l'utilisateur en affichant un simple message..

Je souhaite ajouter une touche finale à notre application pour améliorer l'expérience utilisateur et également pour suivre les directives d'Apple. Lorsque nous faisons une demande de géocodage, nous ne recevons pas de réponse immédiate. Comme je l'ai mentionné plus tôt, la structure Core Location communique avec un service Web et reçoit une réponse. Lorsque la réponse arrive dépend de divers facteurs, tels que la vitesse de notre connexion réseau. La documentation de CLGeocoder stipule que les demandes à la fonction Web doivent être effectuées avec parcimonie. En d'autres termes, l'utilisateur (1) ne doit pas effectuer plusieurs demandes dans un intervalle de temps court en appuyant plusieurs fois sur le bouton et (2) l'utilisateur ne doit pas être en mesure de faire une demande avant que la demande active ait renvoyé une réponse. Pour ce faire, nous désactivons le bouton jusqu'à ce que la demande soit terminée (avec succès ou non). Pour ce faire, nous désactivons le bouton avant de faire la demande et réactivons le bouton dans le bloc d'achèvement. Jetez un coup d’œil à la mise en œuvre complète de notre fetchCoordinates: méthode de clarification. Exécutez votre application et entrez une adresse pour la mettre à l'épreuve..

 - (IBAction) fetchCoordinates: (id) expéditeur if (! Self.geocoder) self.geocoder = [[CLGeocoder alloc] init];  NSString * address = [NSString stringWithFormat: @ "% @% @% @", self.streetField.text, self.cityField.text, self.countryField.text]; self.fetchCoordinatesButton.enabled = NO; [self.geocoder geocodeAddressString: address completionHandler: ^ (repères NSArray *, erreur NSError *) if ([nombre de repères]]> 0) CLPlacemark * placemark = [repères objectAtIndex: 0]; CLLocation * location = placemark.location; CLLocationCoordinate2D Coordinate = location.coordinate; self.coordinatesLabel.text = [NSString stringWithFormat: @ "% f,% f", coordonnée.latitude, coordonnée.longitude]; if ([nombre de placemark.areasOfInterest]> 0) NSString * areaOfInterest = [placemark.areasOfInterest objectAtIndex: 0]; self.nameLabel.text = areaOfInterest;  self.fetchCoordinatesButton.enabled = YES; ]; 

Nous pourrions aller plus loin en affichant un indicateur d'activité pendant la demande et en masquant le bouton, mais je vous laisse le soin de relever le défi. J'ai ajouté cette fonctionnalité au code source qui accompagne ce tutoriel..


Conclusion

Core Location est devenu un cadre très puissant et CLGeocoder n’est que l’une des nombreuses classes qui vous aident à accomplir une tâche complexe avec facilité et très peu de frais généraux. Prendre plaisir!