
Vor einigen Jahren arbeitete ich an einem Projekt, das ähnliche Funktionen mit Snapchat haben und gleichzeitig eine eng thematische Instagram-ähnliche Anwendung sein sollte. Das Projekt wurde nur für eine Plattform entwickelt - iOS. Natürlich wollte der Kunde während der Entwicklung des Hauptfeatures - dem Veröffentlichen von Fotos - plötzlich die Möglichkeit hinzufügen, den Ort zu markieren, an dem das Foto aufgenommen wurde. In den meisten Fällen rufen viele sofort die Places-API von Google und Facebook zurück, aber der Kunde war nicht zufrieden mit der Tatsache, dass diese Lösungen bestimmte Grenzen hatten. Daher wurde nach einer zusätzlichen Überprüfung eine andere Alternative von Apple gefunden -
CLPlacemark , die kostenlos war, und zu diesem Zeitpunkt wurden in der Dokumentation keine Grenzwerte für den täglichen Gebrauch erwähnt. Da die Entwicklung für andere Plattformen ursprünglich überhaupt nicht geplant war, schien dies eine sehr geeignete Option zu sein.
Die Dokumentation von Apple zeigt, dass CLPlacemark viele Details zu diesem Punkt enthalten kann, und
CLGeocoder verfügt auch über eine
Methode , mit der Sie das CLPlacemark-Array mit den erforderlichen Daten nach Standortname einfach zurückgeben können. Wie sich herausstellte, funktioniert alles nicht so rosig.
Der Quellcode sah ungefähr so aus:
import CoreLocation let geocoder = CLGeocoder() func findPlace(name: String) { geocoder.geocodeAddressString(name) { placemarks, error in print(placemarks) } } findPlace(name: “New”)
In diesem einfachen Szenario gibt Geocoder immer ein CLPlacemark-Array zurück, aber der Haken ist, dass dieses Array nie mehr als ein Element enthält. Infolgedessen erhielt ich auf dem gesamten Bildschirm, auf dem eine große Liste von Placements erwartet wurde, wie z. B. New York, New Zeland, New Balance Store usw., nur ein Element, das für meine Eingabe nicht immer relevant war.
Nach einigen erfolglosen Kämpfen mit CLGeocoder sagte mir mein Kollege: "Und Sie haben nicht daran gedacht, nachzuschauen, vielleicht hat MapKit eine ähnliche Gelegenheit?" Wie sich herausstellte, hat
MapKit MKLocalSearch , wo wir das
MKPlacemark- Array erhalten können, das von CLPlacemark geerbt wird. Die Schaltung sah ziemlich gut aus, also fing ich an, diesen Ansatz zu versuchen:
import MapKit let request = MKLocalSearchRequest() var localSearch: MKLocalSearch? func findPlace(name: String) { request.naturalLanguageQuery = text localSearch = MKLocalSearch(request: request) localSearch?.start { (searchResponse, _) in guard let items = searchResponse?.mapItems else { return } print(items) } } findPlace(name: “New)
In diesem Fall erhielt ich als Antwort ein Array mit 10 CLPlacemark-Elementen. Dieses Ergebnis schien akzeptabler zu sein, da als Ergebnis eine ausreichende Liste bereitgestellt wurde. Wenn Sie jedoch den Namen einer der in der Nähe befindlichen Einrichtungen eingeben, wird bei weitem nicht immer das gewünschte Ergebnis angezeigt. Zum Beispiel ist Domino's Pizza neben mir. Ich möchte, dass ich, wenn ich eine solche Abfrage in eine Zeile eingebe, zunächst Einrichtungen bekomme, die mir so nahe wie möglich sind.
Ich begann zu untersuchen, was das Array bildet und wie es verbessert werden kann. Ich habe verschiedene Dinge identifiziert, die die Auswahl der Parameter beeinflussen können:
- Die IP-Adresse, von der aus die Anforderung an Apple gesendet wird. Beim Einschalten des VPN befanden sich die Objekte in den Ergebnissen bereits näher am Standort des VPN-Servers.
- Der aktuelle Standort des Benutzers. Wenn die aktuellen Benutzerkoordinaten an die Anforderung gesendet werden, sind die Ergebnisse viel genauer.
- Die Systemsprache des Geräts.
VPN-BeispieleNew York
Toronto
Kiew
London
Frankfurt
Es ist möglich, dass andere Faktoren die Suchergebnisse beeinflussen können, aber dies hat mir gereicht, um das gewünschte Ergebnis zu erzielen.
Der Weiterentwicklungskurs war die Verwendung des aktuellen Standorts des Geräts.
import UIKit import MapKit import CoreLocation final class ViewController: UIViewController, CLLocationManagerDelegate { private let locationManager = CLLocationManager() private let request = MKLocalSearch.Request() private var localSearch: MKLocalSearch? private var region = MKCoordinateRegion() override func viewDidLoad() { super.viewDidLoad() if CLLocationManager.locationServicesEnabled() { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } } func searchPlace(_ place: String) { localSearch?.cancel() request.naturalLanguageQuery = place request.region = region localSearch = MKLocalSearch(request: request) localSearch?.start { [weak self] response, error in let mapItems = response.mapItems
ErgebnisDer aktuelle Standort des Geräts - Madrid, Internetprovider Vodafone ES

In der Delagate-Methode didUpdateLocations erstellen wir
MKCoordinateSpan . Wenn ich die Apple-Dokumentation richtig verstanden habe, wird unsere aktuelle Region umso enger (und genauer) angezeigt, je niedriger der von uns eingestellte Breiten- / Längengrad Delta ist, da es sich um eine Art Zoom auf unsere aktuellen Koordinaten in MapKit handelt.
Danach änderte sich tatsächlich die Priorität der Auslieferung und zeigte mir vor allem die Orte, die neben mir liegen.
Es bleibt nur, um die Namen in der Liste schöner zu machen. Da einige Eigenschaften von CLPlacemark manchmal denselben Namen haben, sieht es am Ende nicht besonders gut aus: New York, New York, NY. Dazu müssen Sie eine separate Struktur erstellen, die einen schönen Namen in der Liste bildet.
import Foundation import MapKit struct Placemark { let location: String init(item: MKMapItem) { var locationString: String = "" if let name = item.name { locationString += "\(name)" } if let locality = item.placemark.locality, locality != item.name { locationString += ", \(locality)" } if let administrativeArea = item.placemark.administrativeArea, administrativeArea != item.placemark.locality { locationString += ", \(administrativeArea)" } if let country = item.placemark.country, country != item.name { locationString += ", \(country)" } location = locationString } }
Bereits als Antwort auf die Suche können wir CLPlacemark einfach der erstellten Struktur zuordnen und an die Liste übergeben.
localSearch?.start { [weak self] searchResponse, error in guard let items = searchResponse?.mapItems else { return }
Jetzt sehen die Ergebnisse eleganter aus und können bereits im Projekt verwendet werden, um die besuchten Standorte zu markieren.
Einer der Hauptnachteile ist, dass Sie diese Lösung nur verwenden können, wenn das Projekt für iOS / Mac OS geschärft ist. Wenn das Projekt die Entwicklung für andere Plattformen umfasst, würde ich die Verwendung einer Lösung von
Google oder
Facebook empfehlen. Außerdem sind nicht alle Standorte in allen Regionen ideal definiert.
Sie können den endgültigen Code des Projekts im
Repository sehen .