使用苹果的故乡

图片 几年前,我正在从事一个项目,该项目应该具有与Snapchat类似的功能,同时又是一个主题狭窄的类似Instagram的应用程序。 该项目仅针对iOS平台开发。 自然地,在开发主要功能(发布照片)期间,客户突然想添加标记照片位置的功能。 在大多数情况下,许多人会立即从Google和Facebook上调用Places API,但是客户对这些解决方案具有一定限制感到不满意。 因此,经过进一步审查,发现了Apple的另一个替代产品-CLPlacemark ,它是免费的,并且在文档中当时没有提及日常使用的限制。 由于最初根本没有计划开发其他平台,因此这似乎是一个非常合适的选择。

苹果的文档显示,CLPlacemark可以提供有关该点的许多详细信息,并且CLGeocoder还提供了一种方法 ,该方法使您可以轻松地按位置名称返回带有所需数据的CLPlacemark数组。 事实证明,这一切并不那么乐观。

源代码如下所示:

import CoreLocation let geocoder = CLGeocoder() func findPlace(name: String) { geocoder.geocodeAddressString(name) { placemarks, error in print(placemarks) } } findPlace(name: “New”) 

在这种简单情况下,地址解析器始终返回一个CLPlacemark数组,但要注意的是,该数组永远不会包含多个元素。 结果,在整个屏幕上,预期会有大量的展示位置,例如:纽约,新西兰,新百伦商店等,我只收到一个与输入内容并不总是相关的元素。

在与CLGeocoder进行了一些失败的斗争之后,我的同事告诉我:“您没有想到尝试一下,也许MapKit也有类似的机会?” 事实证明,MapKit具有MKLocalSearch ,在这里我们可以获取MKPlacemark数组,该数组是从CLPlacemark继承的。 电路看起来很正常,所以我开始尝试这种方法:

 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) 

结果

在这种情况下,我收到了一个包含10个CLPlacemark元素的数组作为响应。 由于提供了足够的清单,因此该结果似乎更可接受。 但并非总是如此,当您开始输入附近任何机构的名称时,它立即显示出所需的结果。 例如,我旁边是Domino's Pizza。 我想当我在一行中输入这样的查询时,首先,我应该获得离我尽可能近的场所。

我开始根据数组形成的形式以及如何对其进行改进进行研究。 我发现了几件事会影响参数的选择:

  1. 向Apple发出请求的IP地址。 打开VPN时,结果中的对象已经更靠近VPN服务器的位置。
  2. 用户的当前位置。 如果将当前用户坐标发送给请求,则结果将更加准确。
  3. 设备的系统语言。

VPN实例

VPN实例
纽约

多伦多

基辅

伦敦的

法兰克福


可能还有其他因素可能会影响搜索结果,但这足以让我获得所需的结果。

进一步的开发过程是使用设备的当前位置。

 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 //     MKMapItem } } // MARK: - CLLocationManagerDelegate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let lastLocation = locations.last else { return } let span = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5) region = MKCoordinateRegion(center: lastLocation.coordinate, span: span) } } 

结果
设备的当前位置-马德里,互联网提供商Vodafone ES

在delagate方法didUpdateLocations中,我们创建MKCoordinateSpan 。 如果我正确理解了Apple文档,则我们设置的纬度/经度Delta值越低,将指示出当前区域越窄(更准确),因为这是MapKit中当前坐标的一种缩放。
在那之后,确实,引渡的优先级发生了变化,并向我主要展示了我附近的那些地方。

只是使列表中的名称更美观而已。 由于有时,CLPlacemark的某些属性可能具有相同的名称,最终看起来会不太好:纽约,纽约,纽约。 为此,您需要创建一个单独的结构,该结构将在列表中形成一个漂亮的名称。

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

然后,已经响应了搜索,我们可以轻松地将CLPlacemark映射到创建的结构并将其传递到列表。

 localSearch?.start { [weak self] searchResponse, error in guard let items = searchResponse?.mapItems else { return } //  CLPlacemark    let placemarks = items.map { Placemark(item: $0) } } 

现在,结果看起来更加优雅,可以在项目中用于标记其访问的位置。

主要缺点之一是,只有在针对iOS / Mac OS改进了项目的情况下,才可以使用此解决方案。 如果该项目涉及其他平台的开发,我建议使用GoogleFacebook的解决方案。 此外,并非所有区域都理想地定义了所有区域。

您可以在存储库中看到项目的最终代码。

Source: https://habr.com/ru/post/zh-CN462465/


All Articles