ios Swift - 从反向地理编码生成地址格式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/41358423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift - Generate an Address Format from Reverse Geocoding
提问by Dinuka Jay
I am trying to generate a Formatted Full address using CLGeocoder in Swift 3. I referred to this SOthread to get the code given below.
我正在尝试在 Swift 3 中使用 CLGeocoder 生成格式化的完整地址。我参考了这个SO线程来获取下面给出的代码。
However, sometimes the app crashes with a 'nil' error at the line:
但是,有时应用程序会崩溃并显示“nil”错误:
//Address dictionary
print(placeMark.addressDictionary ?? "")
Questions:
问题:
- How can I concatenate these values retrieved from the GeoCoder to form a full address? (Street + City + etc)
- How do I handle the nil error I get when the func is unable to find an address?
- 如何连接从 GeoCoder 检索到的这些值以形成完整地址?(街道+城市+等)
- 当 func 无法找到地址时,我如何处理我得到的 nil 错误?
Full code:
完整代码:
func getAddress() -> String {
var address: String = ""
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
//selectedLat and selectedLon are double values set by the app in a previous process
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// Address dictionary
//print(placeMark.addressDictionary ?? "")
// Location name
if let locationName = placeMark.addressDictionary!["Name"] as? NSString {
//print(locationName)
}
// Street address
if let street = placeMark.addressDictionary!["Thoroughfare"] as? NSString {
//print(street)
}
// City
if let city = placeMark.addressDictionary!["City"] as? NSString {
//print(city)
}
// Zip code
if let zip = placeMark.addressDictionary!["ZIP"] as? NSString {
//print(zip)
}
// Country
if let country = placeMark.addressDictionary!["Country"] as? NSString {
//print(country)
}
})
return address;
}
回答by Himanshu Moradiya
func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
let lat: Double = Double("\(pdblLatitude)")!
//21.228124
let lon: Double = Double("\(pdblLongitude)")!
//72.833770
let ceo: CLGeocoder = CLGeocoder()
center.latitude = lat
center.longitude = lon
let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)
ceo.reverseGeocodeLocation(loc, completionHandler:
{(placemarks, error) in
if (error != nil)
{
print("reverse geodcode fail: \(error!.localizedDescription)")
}
let pm = placemarks! as [CLPlacemark]
if pm.count > 0 {
let pm = placemarks![0]
print(pm.country)
print(pm.locality)
print(pm.subLocality)
print(pm.thoroughfare)
print(pm.postalCode)
print(pm.subThoroughfare)
var addressString : String = ""
if pm.subLocality != nil {
addressString = addressString + pm.subLocality! + ", "
}
if pm.thoroughfare != nil {
addressString = addressString + pm.thoroughfare! + ", "
}
if pm.locality != nil {
addressString = addressString + pm.locality! + ", "
}
if pm.country != nil {
addressString = addressString + pm.country! + ", "
}
if pm.postalCode != nil {
addressString = addressString + pm.postalCode! + " "
}
print(addressString)
}
})
}
回答by Gerd Castan
Formatting addresses is hard because each country has its own format.
格式化地址很困难,因为每个国家都有自己的格式。
With a few lines of code, you can get the correct address format for each country and let Apple handle the differences.
只需几行代码,您就可以获得每个国家/地区的正确地址格式,并让 Apple 处理差异。
Since iOS 11, you can get a Contacts framework address:
从 iOS 11 开始,您可以获得 Contacts 框架地址:
extension CLPlacemark {
@available(iOS 11.0, *)
open var postalAddress: CNPostalAddress? { get }
}
This extension is part of the Contacts
framework.
This means, this feature is invisible to you in the XCode
code completion until you do
这个扩展是Contacts
框架的一部分。这意味着,此功能在XCode
代码完成中对您不可见,直到您执行
import Contacts
With this additional import, you can do something like
通过这个额外的导入,你可以做一些类似的事情
CLGeocoder().reverseGeocodeLocation(location, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
guard let place = clPlacemark?.first else {
print("No placemark from Apple: \(String(describing: error))")
return
}
let postalAddressFormatter = CNPostalAddressFormatter()
postalAddressFormatter.style = .mailingAddress
var addressString: String?
if let postalAddress = place.postalAddress {
addressString = postalAddressFormatter.string(from: postalAddress)
}
}
and get the address formatted in the format for the country in the address.
并获取以地址china家/地区格式格式化的地址。
The formatter even supports formatting as an attributedString.
格式化程序甚至支持格式化为一个属性字符串。
Prior to iOS 11, you can convert CLPlacemark
to CNPostalAddress
yourself and still can use the country specific formatting of CNPostalAddressFormatter
.
在 iOS 11 之前,您可以转换CLPlacemark
为CNPostalAddress
自己,并且仍然可以使用CNPostalAddressFormatter
.
回答by oscar castellon
This is my code for swift 3
这是我的 swift 3 代码
func getAdressName(coords: CLLocation) {
CLGeocoder().reverseGeocodeLocation(coords) { (placemark, error) in
if error != nil {
print("Hay un error")
} else {
let place = placemark! as [CLPlacemark]
if place.count > 0 {
let place = placemark![0]
var adressString : String = ""
if place.thoroughfare != nil {
adressString = adressString + place.thoroughfare! + ", "
}
if place.subThoroughfare != nil {
adressString = adressString + place.subThoroughfare! + "\n"
}
if place.locality != nil {
adressString = adressString + place.locality! + " - "
}
if place.postalCode != nil {
adressString = adressString + place.postalCode! + "\n"
}
if place.subAdministrativeArea != nil {
adressString = adressString + place.subAdministrativeArea! + " - "
}
if place.country != nil {
adressString = adressString + place.country!
}
self.lblPlace.text = adressString
}
}
}
}
You can esaily call above funcation like:
您可以轻松调用上述函数,例如:
let cityCoords = CLLocation(latitude: newLat, longitude: newLon)
cityData(coord: cityCoords)
回答by Makaille
To concatenate you can simply replace return address
by this :
要连接,您可以简单地替换return address
为:
return "\(locationName), \(street), \(city), \(zip), \(country)"
回答by Midhun MP
- For fixing the empty address issue, either you can use a class property to hold the appended value or you can use a closure to return the value back to the calling function
- For fixing the crash you need to avoid the force unwrapping of optionals
- 为了解决空地址问题,您可以使用类属性来保存附加值,也可以使用闭包将值返回给调用函数
- 为了修复崩溃,您需要避免强制展开选项
Using a closure you can do it like:
使用闭包你可以这样做:
// Using closure
func getAddress(handler: (String) -> Void)
{
var address: String = ""
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
//selectedLat and selectedLon are double values set by the app in a previous process
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark?
placeMark = placemarks?[0]
// Address dictionary
//print(placeMark.addressDictionary ?? "")
// Location name
if let locationName = placeMark?.addressDictionary?["Name"] as? String {
address += locationName + ", "
}
// Street address
if let street = placeMark?.addressDictionary?["Thoroughfare"] as? String {
address += street + ", "
}
// City
if let city = placeMark?.addressDictionary?["City"] as? String {
address += city + ", "
}
// Zip code
if let zip = placeMark?.addressDictionary?["ZIP"] as? String {
address += zip + ", "
}
// Country
if let country = placeMark?.addressDictionary?["Country"] as? String {
address += country
}
// Passing address back
handler(address)
})
}
You can call the method like:
您可以调用该方法,如:
getAddress { (address) in
print(address)
}
回答by elarctheitroadis
Keeping it simple - A full Swift 3 & 4 compatible View Controller example for obtaining a formatted address string from user's location (add in the other keys available in CLPlacemarkif you want more information in your string):
保持简单 - 一个完整的 Swift 3 & 4 兼容视图控制器示例,用于从用户的位置获取格式化的地址字符串(如果您想在字符串中获得更多信息,请添加CLPlacemark 中可用的其他键):
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let manager = CLLocationManager()
let geocoder = CLGeocoder()
var locality = ""
var administrativeArea = ""
var country = ""
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
manager.stopUpdatingLocation()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in
if (error != nil) {
print("Error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count > 0 {
let placemark = placemarks![0]
self.locality = placemark.locality!
self.administrativeArea = placemark.administrativeArea!
self.country = placemark.country!
}
})
}
func userLocationString() -> String {
let userLocationString = "\(locality), \(administrativeArea), \(country)"
return userLocationString
}
}
Calling print(userLocationString()) in this example will print: suburb, state, country
在本例中调用 print(userLocationString()) 将打印:郊区、州、国家
Don't forget to add Privacy - Location When In Use Usage Descriptionto your Info.plist file beforehand, to allow the user to grant permissions to your app to utilise location services.
不要忘记事先将Privacy - Location When In Use Usage Description 添加到您的 Info.plist 文件中,以允许用户授予您的应用程序使用位置服务的权限。
回答by Prashant Bhayani
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: vehicleLocation.latitude, longitude: vehicleLocation.latitude), completionHandler: {(placemarks, error) -> Void in
guard error == nil else {completionHandler(nil); return}
guard let place = placemarks else {completionHandler(nil); return}
if place.count > 0 {
let pm = place[0]
var addArray:[String] = []
if let name = pm.name {
addArray.append(name)
}
if let thoroughfare = pm.thoroughfare {
addArray.append(thoroughfare)
}
if let subLocality = pm.subLocality {
addArray.append(subLocality)
}
if let locality = pm.locality {
addArray.append(locality)
}
if let subAdministrativeArea = pm.subAdministrativeArea {
addArray.append(subAdministrativeArea)
}
if let administrativeArea = pm.administrativeArea {
addArray.append(administrativeArea)
}
if let country = pm.country {
addArray.append(country)
}
if let postalCode = pm.postalCode {
addArray.append(postalCode)
}
let addressString = addArray.joined(separator: ",\n")
print(addressString)
completionHandler(addressString)
}
else { completionHandler(nil)}
})
回答by Javier Amor Penas
I create my own static class for Geocoding and get attributes of CLPlacemark and obtain a complete address, like "usually" returns Google:
我为地理编码创建了自己的静态类并获取 CLPlacemark 的属性并获取完整的地址,例如“通常”返回 Google:
import Foundation
import CoreLocation
class ReverseGeocoding {
static func geocode(latitude: Double, longitude: Double, completion: @escaping (CLPlacemark?, _ completeAddress: String?, Error?) -> ()) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemarks, error in
guard let placemark = placemarks?.first, error == nil else {
completion(nil, nil, error)
return
}
let completeAddress = getCompleteAddress(placemarks)
completion(placemark, completeAddress, nil)
}
}
static private func getCompleteAddress(_ placemarks: [CLPlacemark]?) -> String {
guard let placemarks = placemarks else {
return ""
}
let place = placemarks as [CLPlacemark]
if place.count > 0 {
let place = placemarks[0]
var addressString : String = ""
if place.thoroughfare != nil {
addressString = addressString + place.thoroughfare! + ", "
}
if place.subThoroughfare != nil {
addressString = addressString + place.subThoroughfare! + ", "
}
if place.locality != nil {
addressString = addressString + place.locality! + ", "
}
if place.postalCode != nil {
addressString = addressString + place.postalCode! + ", "
}
if place.subAdministrativeArea != nil {
addressString = addressString + place.subAdministrativeArea! + ", "
}
if place.country != nil {
addressString = addressString + place.country!
}
return addressString
}
return ""
}
}
Then the implementation:
然后实现:
ReverseGeocoding.geocode(coordinate: coordinate, completion: { (placeMark, completeAddress, error) in
if let placeMark = placeMark, let completeAddress = completeAddress {
print(placeMark.postalCode)
print(placeMark)
print(completeAddress)
} else {
// do something with the error
}
Finaly the print:
最后打印:
15172
Calle del Arenal, 4, Calle del Arenal, 4, 15172 Oleiros, A Coru?a, Espa?a @ <+43.33190337,-8.37144380> +/- 100.00m, region CLCircularRegion (identifier:'<+43.33190337,-8.37144380> radius 70.84', center:<+43.33190337,-8.37144380>, radius:70.84m)
Calle del Arenal, 4, Oleiros, 15172, A Coru?a, Espa?a
回答by Saud Waqar
Here's a 2-3 line version of the answers here:
这是这里答案的 2-3 行版本:
func getAddress(placemarks: [CLPlacemark]) -> String {
guard let placemark = placemarks.first, !placemarks.isEmpty else {return ""}
let outputString = [placemark.locality,
placemark.subLocality,
placemark.thoroughfare,
placemark.postalCode,
placemark.subThoroughfare,
placemark.country].compactMap{func convertLatLongToAddress(latitude:Double, longitude:Double) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: latitude, longitude: longitude)
var labelText = ""
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if placeMark != nil {
if let name = placeMark.name {
labelText = name
}
if let subThoroughfare = placeMark.subThoroughfare {
if (subThoroughfare != placeMark.name) && (labelText != subThoroughfare) {
labelText = (labelText != "") ? labelText + "," + subThoroughfare : subThoroughfare
}
}
if let subLocality = placeMark.subLocality {
if (subLocality != placeMark.subThoroughfare) && (labelText != subLocality) {
labelText = (labelText != "") ? labelText + "," + subLocality : subLocality
}
}
if let street = placeMark.thoroughfare {
if (street != placeMark.subLocality) && (labelText != street) {
labelText = (labelText != "") ? labelText + "," + street : street
}
}
if let locality = placeMark.locality {
if (locality != placeMark.thoroughfare) && (labelText != locality) {
labelText = (labelText != "") ? labelText + "," + locality : locality
}
}
if let city = placeMark.subAdministrativeArea {
if (city != placeMark.locality) && (labelText != city) {
labelText = (labelText != "") ? labelText + "," + city : city
}
}
if let state = placeMark.postalAddress?.state {
if (state != placeMark.subAdministrativeArea) && (labelText != state) {
labelText = (labelText != "") ? labelText + "," + state : state
}
}
if let country = placeMark.country {
labelText = (labelText != "") ? labelText + "," + country : country
}
// labelText gives you the address of the place
}
})
}
}.joined(separator: ", ")
print(outputString)
return outputString
}```
回答by Wimukthi Rajapaksha
Here as an improvement I added place name as well. It makes address more meaningful.
作为改进,我还添加了地名。它使地址更有意义。