Swift 4 中的反向地理编码
Reverse geocoding in Swift 4
我正在尝试编写一个简单的方法,该方法提供 CLLocationDegrees
和 returns 一个 CLPlacemark
。看着Apple's documentation,好像是个简单的任务。
下面是我扔到操场上的东西:
import CoreLocation
// this is necessary for async code in a playground
import PlaygroundSupport
// this is necessary for async code in a playground
PlaygroundPage.current.needsIndefiniteExecution = true
func geocode(latitude: CLLocationDegrees, longitude: CLLocationDegrees) -> CLPlacemark? {
let location = CLLocation(latitude: latitude, longitude: longitude)
let geocoder = CLGeocoder()
var placemark: CLPlacemark?
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if error != nil {
print("something went horribly wrong")
}
if let placemarks = placemarks {
placemark = placemarks.first
}
}
return placemark
}
let myPlacemark = geocode(latitude: 37.3318, longitude: 122.0312)
就目前而言,我的方法返回零。我不确定我的错误在哪里,但我确信这对我来说是非常愚蠢的事情。感谢阅读。
CLGeocoder 方法是异步的。您需要一个完成处理程序:
import UIKit
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemark, error in
guard let placemark = placemark, error == nil else {
completion(nil, error)
return
}
completion(placemark, nil)
}
}
或者简单地说:
func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: completion)
}
或扩展 CLLocation:
extension CLLocation {
func geocode(completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
要将地标格式化为邮寄地址,您可以使用联系人框架 CNPostalAddressFormatter
:
import Contacts
extension Formatter {
static let mailingAddress: CNPostalAddressFormatter = {
let formatter = CNPostalAddressFormatter()
formatter.style = .mailingAddress
return formatter
}()
}
extension CLPlacemark {
var mailingAddress: String? {
postalAddress?.mailingAddress
}
}
extension CNPostalAddress {
var mailingAddress: String {
Formatter.mailingAddress.string(from: self)
}
}
placemark
Contains an array of CLPlacemark objects. For most geocoding requests,
this array should contain only one entry. However, forward-geocoding
requests may return multiple placemark objects in situations where the
specified address could not be resolved to a single location. If the
request was canceled or there was an error in obtaining the placemark
information, this parameter is nil.
有关 CLPlacemark 属性的更多信息,您可以查看此 CLPlacemark
用法:
let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
location.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
// you should always update your UI in the main thread
DispatchQueue.main.async {
// update UI here
print("name:", placemark.name ?? "unknown")
print("address1:", placemark.thoroughfare ?? "unknown")
print("address2:", placemark.subThoroughfare ?? "unknown")
print("neighborhood:", placemark.subLocality ?? "unknown")
print("city:", placemark.locality ?? "unknown")
print("state:", placemark.administrativeArea ?? "unknown")
print("subAdministrativeArea:", placemark.subAdministrativeArea ?? "unknown")
print("zip code:", placemark.postalCode ?? "unknown")
print("country:", placemark.country ?? "unknown", terminator: "\n\n")
print("isoCountryCode:", placemark.isoCountryCode ?? "unknown")
print("region identifier:", placemark.region?.identifier ?? "unknown")
print("timezone:", placemark.timeZone ?? "unknown", terminator:"\n\n")
// Mailind Address
print(placemark.mailingAddress ?? "unknown")
}
}
}
这将打印
name: Morro da Saudade
address1: Rua Casuarina
address2: 597
neighborhood: Lagoa
city: Rio de Janeiro
state: RJ
subAdministrativeArea: unknown
zip code: 22011-040
country: Brazil
isoCountryCode: BR
region identifier: <-22.96345100,-43.19824200> radius 141.83
timezone: America/Sao_Paulo (current)
Rua Casuarina, 597
Lagoa
Rio de Janeiro RJ
22011-040
Brazil
Stack Overflow 上有很多关于 'reverseGeocodeLocation' 的问题,我见过很多。我正在使用 Swift 4.2,我认为我的 'reverseGeocodeLocation' 请求可能会超时。好吧,情况并非如此,当开始以 iPhone 移动时,'didUpdateLocations' 周期需要成熟。在 'reverseGeocodeLocation' returns 个地标之前可能需要两个或更多周期。
我写了一个简单的应用程序,它使用 'reverseGeocodeLocation' 直到 'placemarks' 在重复 'didUpdateLocations' 循环后返回。这是我自己种的 'SampleCode'。我从使用该应用程序中学到了很多东西,它可能会帮助其他人了解反向地理编码请求在现实世界中的工作原理。
请就可能的改进发表您的意见。此代码免费提供和免费使用。
import Foundation
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
@IBAction func RefreshLocationButton(_ sender: Any) {
self.requestingPlacemark = true
self.placemarkData = nil
//^Make another Placemark Request
self.requestCounter = 0
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}
@IBOutlet weak var LocationCounterLabel: UILabel!
@IBOutlet weak var RequestCounterLabel: UILabel!
@IBOutlet weak var LocationLabel: UILabel!
@IBOutlet weak var PlacemarkLabel: UILabel!
let locationManager = CLLocationManager()
var placemarkData: CLPlacemark!
var placemarkString: String!
var printPlacemarkData: Bool!
var didUpdateLocationsCounter: Int = 0
var requestCounter: Int = 0
var requestingPlacemark: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
//locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.startUpdatingLocation()
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}}
func printPlacemarks() {
if printPlacemarkData {
self.placemarkString = "Placemark Data:"
self.placemarkString = buildPlacemarkString(item: 1)
self.placemarkString = buildPlacemarkString(item: 2)
self.placemarkString = buildPlacemarkString(item: 3)
self.placemarkString = buildPlacemarkString(item: 4)
self.placemarkString = buildPlacemarkString(item: 5)
self.placemarkString = buildPlacemarkString(item: 6)
self.placemarkString = buildPlacemarkString(item: 7)
self.placemarkString = buildPlacemarkString(item: 8)
self.placemarkString = buildPlacemarkString(item: 9)
self.placemarkString = buildPlacemarkString(item: 10)
self.PlacemarkLabel.text = self.placemarkString
self.printPlacemarkData = false}}
func buildPlacemarkString(item: Int) -> String {
var elementText: String!
var newString: String!
switch item {
case 1: elementText = "name: " + self.placemarkData.name!
case 2: elementText = "subThoroughfare: " + self.placemarkData.subThoroughfare!
case 3: elementText = "thoroughfare: " + self.placemarkData.thoroughfare!
case 4: elementText = "postalCode: " + self.placemarkData.postalCode!
case 5: elementText = "subLocality: " + self.placemarkData.subLocality!
case 6: elementText = "locality: " + self.placemarkData.locality!
case 7: elementText = "subAdministrativeArea: " + self.placemarkData.subAdministrativeArea!
case 8: elementText = "administrativeArea: " + self.placemarkData.administrativeArea!
case 9: elementText = "country: " + self.placemarkData.country!
case 10: elementText = "isoCountryCode: " + self.placemarkData.isoCountryCode!
default: print("Error: incorrect item number!")}
newString = self.placemarkString + "\n" + elementText
return newString
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//location prints one time and 'didUpdateLocations' is stopped:
self.didUpdateLocationsCounter = self.didUpdateLocationsCounter + 1
let labelString = String(describing: self.didUpdateLocationsCounter)
self.LocationCounterLabel.text = "Location Update Counter: " + labelString
if self.placemarkData == nil {
//location variable:
self.requestCounter = self.requestCounter + 1
let textString = String(describing: self.requestCounter)
self.RequestCounterLabel.text = "Request Counter: " + textString
if locations.count == 0 {
self.LocationLabel.text = "Locations: There was NONE"}
else {
if locations.count > 0 {
let coordinate2D: CLLocation = locations.first!
let location = CLLocation(latitude: coordinate2D.coordinate.latitude, longitude: coordinate2D.coordinate.longitude)
let printString = String(describing: location)
self.LocationLabel.text = "Location: " + printString
let geocoder: CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if error != nil {
let errorString = String(describing: error?.localizedDescription)
print("reverse geodcode fail: \(errorString)")
self.LocationCounterLabel.text = ""
self.RequestCounterLabel.text = ""
self.LocationLabel.text = "Reverse Geodcode fail: \(errorString)"
self.PlacemarkLabel.text = ""
self.requestingPlacemark = false
return}
else {
let pm = placemarks! as [CLPlacemark]
//There is ALWAYS 'placemarks' Data
if pm.count > 0 {
self.placemarkData = placemarks![0]
self.printPlacemarkData = true
self.printPlacemarks()
self.requestingPlacemark = false}}})}
else {
if self.requestingPlacemark {
self.LocationLabel.text = "Problem: There is no 'location.first'"}}}}}}
然后是情节提要 UI:
Storyboard Image
View Controller Image
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="MyThoroughfare" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Counter" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZIg-u9-TMR">
<rect key="frame" x="16" y="20" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Count Label" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="csF-KS-xGE">
<rect key="frame" x="16" y="48" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location String" lineBreakMode="tailTruncation" numberOfLines="8" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qOc-gk-gSp">
<rect key="frame" x="16" y="76" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Placemark String" lineBreakMode="tailTruncation" numberOfLines="14" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="42A-nO-tNf">
<rect key="frame" x="16" y="104" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HgN-oB-ds0">
<rect key="frame" x="121.5" y="578" width="132" height="30"/>
<state key="normal" title="Request Placemark"/>
<connections>
<action selector="RefreshLocationButton:" destination="BYZ-38-t0r" eventType="touchUpInside" id="izY-S5-wjg"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="ZIg-u9-TMR" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="6HO-kN-l1z"/>
<constraint firstItem="qOc-gk-gSp" firstAttribute="top" secondItem="csF-KS-xGE" secondAttribute="bottom" constant="7.5" id="6ux-dx-gJr"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="42A-nO-tNf" secondAttribute="trailing" constant="16" id="8dG-gV-Cob"/>
<constraint firstItem="HgN-oB-ds0" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="A38-Nt-i3D"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="ZIg-u9-TMR" secondAttribute="trailing" constant="16" id="Bb0-YC-oov"/>
<constraint firstItem="csF-KS-xGE" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="FJU-ha-HkL"/>
<constraint firstItem="42A-nO-tNf" firstAttribute="top" secondItem="qOc-gk-gSp" secondAttribute="bottom" constant="7.5" id="SpR-XB-MLG"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="HgN-oB-ds0" secondAttribute="bottom" constant="59" id="Vr6-QE-230"/>
<constraint firstItem="42A-nO-tNf" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="aHo-kJ-aCb"/>
<constraint firstItem="csF-KS-xGE" firstAttribute="top" secondItem="ZIg-u9-TMR" secondAttribute="bottom" constant="7.5" id="c63-Mq-ItW"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="csF-KS-xGE" secondAttribute="trailing" constant="16" id="dlV-Hc-8XJ"/>
<constraint firstItem="qOc-gk-gSp" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="m5y-Q8-jxI"/>
<constraint firstItem="ZIg-u9-TMR" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="sdw-WQ-Bx9"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="qOc-gk-gSp" secondAttribute="trailing" constant="16" id="woB-ig-i5v"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="LocationCounterLabel" destination="ZIg-u9-TMR" id="K4g-WT-f82"/>
<outlet property="LocationLabel" destination="qOc-gk-gSp" id="Cdw-bw-EWt"/>
<outlet property="PlacemarkLabel" destination="42A-nO-tNf" id="dMh-bw-cRE"/>
<outlet property="RequestCounterLabel" destination="csF-KS-xGE" id="ai6-zn-Toi"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53.600000000000001" y="66.11694152923539"/>
</scene>
</scenes>
</document>
我正在尝试编写一个简单的方法,该方法提供 CLLocationDegrees
和 returns 一个 CLPlacemark
。看着Apple's documentation,好像是个简单的任务。
下面是我扔到操场上的东西:
import CoreLocation
// this is necessary for async code in a playground
import PlaygroundSupport
// this is necessary for async code in a playground
PlaygroundPage.current.needsIndefiniteExecution = true
func geocode(latitude: CLLocationDegrees, longitude: CLLocationDegrees) -> CLPlacemark? {
let location = CLLocation(latitude: latitude, longitude: longitude)
let geocoder = CLGeocoder()
var placemark: CLPlacemark?
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if error != nil {
print("something went horribly wrong")
}
if let placemarks = placemarks {
placemark = placemarks.first
}
}
return placemark
}
let myPlacemark = geocode(latitude: 37.3318, longitude: 122.0312)
就目前而言,我的方法返回零。我不确定我的错误在哪里,但我确信这对我来说是非常愚蠢的事情。感谢阅读。
CLGeocoder 方法是异步的。您需要一个完成处理程序:
import UIKit
import CoreLocation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemark, error in
guard let placemark = placemark, error == nil else {
completion(nil, error)
return
}
completion(placemark, nil)
}
}
或者简单地说:
func geocode(latitude: Double, longitude: Double, completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude), completionHandler: completion)
}
或扩展 CLLocation:
extension CLLocation {
func geocode(completion: @escaping (_ placemark: [CLPlacemark]?, _ error: Error?) -> Void) {
CLGeocoder().reverseGeocodeLocation(self, completionHandler: completion)
}
}
要将地标格式化为邮寄地址,您可以使用联系人框架 CNPostalAddressFormatter
:
import Contacts
extension Formatter {
static let mailingAddress: CNPostalAddressFormatter = {
let formatter = CNPostalAddressFormatter()
formatter.style = .mailingAddress
return formatter
}()
}
extension CLPlacemark {
var mailingAddress: String? {
postalAddress?.mailingAddress
}
}
extension CNPostalAddress {
var mailingAddress: String {
Formatter.mailingAddress.string(from: self)
}
}
placemark
Contains an array of CLPlacemark objects. For most geocoding requests, this array should contain only one entry. However, forward-geocoding requests may return multiple placemark objects in situations where the specified address could not be resolved to a single location. If the request was canceled or there was an error in obtaining the placemark information, this parameter is nil.
有关 CLPlacemark 属性的更多信息,您可以查看此 CLPlacemark
用法:
let location = CLLocation(latitude: -22.963451, longitude: -43.198242)
location.geocode { placemark, error in
if let error = error as? CLError {
print("CLError:", error)
return
} else if let placemark = placemark?.first {
// you should always update your UI in the main thread
DispatchQueue.main.async {
// update UI here
print("name:", placemark.name ?? "unknown")
print("address1:", placemark.thoroughfare ?? "unknown")
print("address2:", placemark.subThoroughfare ?? "unknown")
print("neighborhood:", placemark.subLocality ?? "unknown")
print("city:", placemark.locality ?? "unknown")
print("state:", placemark.administrativeArea ?? "unknown")
print("subAdministrativeArea:", placemark.subAdministrativeArea ?? "unknown")
print("zip code:", placemark.postalCode ?? "unknown")
print("country:", placemark.country ?? "unknown", terminator: "\n\n")
print("isoCountryCode:", placemark.isoCountryCode ?? "unknown")
print("region identifier:", placemark.region?.identifier ?? "unknown")
print("timezone:", placemark.timeZone ?? "unknown", terminator:"\n\n")
// Mailind Address
print(placemark.mailingAddress ?? "unknown")
}
}
}
这将打印
name: Morro da Saudade
address1: Rua Casuarina
address2: 597
neighborhood: Lagoa
city: Rio de Janeiro
state: RJ
subAdministrativeArea: unknown
zip code: 22011-040
country: Brazil
isoCountryCode: BR
region identifier: <-22.96345100,-43.19824200> radius 141.83
timezone: America/Sao_Paulo (current)
Rua Casuarina, 597
Lagoa
Rio de Janeiro RJ
22011-040
Brazil
Stack Overflow 上有很多关于 'reverseGeocodeLocation' 的问题,我见过很多。我正在使用 Swift 4.2,我认为我的 'reverseGeocodeLocation' 请求可能会超时。好吧,情况并非如此,当开始以 iPhone 移动时,'didUpdateLocations' 周期需要成熟。在 'reverseGeocodeLocation' returns 个地标之前可能需要两个或更多周期。
我写了一个简单的应用程序,它使用 'reverseGeocodeLocation' 直到 'placemarks' 在重复 'didUpdateLocations' 循环后返回。这是我自己种的 'SampleCode'。我从使用该应用程序中学到了很多东西,它可能会帮助其他人了解反向地理编码请求在现实世界中的工作原理。
请就可能的改进发表您的意见。此代码免费提供和免费使用。
import Foundation
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
@IBAction func RefreshLocationButton(_ sender: Any) {
self.requestingPlacemark = true
self.placemarkData = nil
//^Make another Placemark Request
self.requestCounter = 0
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}
@IBOutlet weak var LocationCounterLabel: UILabel!
@IBOutlet weak var RequestCounterLabel: UILabel!
@IBOutlet weak var LocationLabel: UILabel!
@IBOutlet weak var PlacemarkLabel: UILabel!
let locationManager = CLLocationManager()
var placemarkData: CLPlacemark!
var placemarkString: String!
var printPlacemarkData: Bool!
var didUpdateLocationsCounter: Int = 0
var requestCounter: Int = 0
var requestingPlacemark: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
//locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.startUpdatingLocation()
self.RequestCounterLabel.text = ""
self.LocationLabel.text = ""
self.PlacemarkLabel.text = ""}}
func printPlacemarks() {
if printPlacemarkData {
self.placemarkString = "Placemark Data:"
self.placemarkString = buildPlacemarkString(item: 1)
self.placemarkString = buildPlacemarkString(item: 2)
self.placemarkString = buildPlacemarkString(item: 3)
self.placemarkString = buildPlacemarkString(item: 4)
self.placemarkString = buildPlacemarkString(item: 5)
self.placemarkString = buildPlacemarkString(item: 6)
self.placemarkString = buildPlacemarkString(item: 7)
self.placemarkString = buildPlacemarkString(item: 8)
self.placemarkString = buildPlacemarkString(item: 9)
self.placemarkString = buildPlacemarkString(item: 10)
self.PlacemarkLabel.text = self.placemarkString
self.printPlacemarkData = false}}
func buildPlacemarkString(item: Int) -> String {
var elementText: String!
var newString: String!
switch item {
case 1: elementText = "name: " + self.placemarkData.name!
case 2: elementText = "subThoroughfare: " + self.placemarkData.subThoroughfare!
case 3: elementText = "thoroughfare: " + self.placemarkData.thoroughfare!
case 4: elementText = "postalCode: " + self.placemarkData.postalCode!
case 5: elementText = "subLocality: " + self.placemarkData.subLocality!
case 6: elementText = "locality: " + self.placemarkData.locality!
case 7: elementText = "subAdministrativeArea: " + self.placemarkData.subAdministrativeArea!
case 8: elementText = "administrativeArea: " + self.placemarkData.administrativeArea!
case 9: elementText = "country: " + self.placemarkData.country!
case 10: elementText = "isoCountryCode: " + self.placemarkData.isoCountryCode!
default: print("Error: incorrect item number!")}
newString = self.placemarkString + "\n" + elementText
return newString
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//location prints one time and 'didUpdateLocations' is stopped:
self.didUpdateLocationsCounter = self.didUpdateLocationsCounter + 1
let labelString = String(describing: self.didUpdateLocationsCounter)
self.LocationCounterLabel.text = "Location Update Counter: " + labelString
if self.placemarkData == nil {
//location variable:
self.requestCounter = self.requestCounter + 1
let textString = String(describing: self.requestCounter)
self.RequestCounterLabel.text = "Request Counter: " + textString
if locations.count == 0 {
self.LocationLabel.text = "Locations: There was NONE"}
else {
if locations.count > 0 {
let coordinate2D: CLLocation = locations.first!
let location = CLLocation(latitude: coordinate2D.coordinate.latitude, longitude: coordinate2D.coordinate.longitude)
let printString = String(describing: location)
self.LocationLabel.text = "Location: " + printString
let geocoder: CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if error != nil {
let errorString = String(describing: error?.localizedDescription)
print("reverse geodcode fail: \(errorString)")
self.LocationCounterLabel.text = ""
self.RequestCounterLabel.text = ""
self.LocationLabel.text = "Reverse Geodcode fail: \(errorString)"
self.PlacemarkLabel.text = ""
self.requestingPlacemark = false
return}
else {
let pm = placemarks! as [CLPlacemark]
//There is ALWAYS 'placemarks' Data
if pm.count > 0 {
self.placemarkData = placemarks![0]
self.printPlacemarkData = true
self.printPlacemarks()
self.requestingPlacemark = false}}})}
else {
if self.requestingPlacemark {
self.LocationLabel.text = "Problem: There is no 'location.first'"}}}}}}
然后是情节提要 UI:
Storyboard Image
View Controller Image
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="MyThoroughfare" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Counter" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZIg-u9-TMR">
<rect key="frame" x="16" y="20" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location Count Label" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="csF-KS-xGE">
<rect key="frame" x="16" y="48" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location String" lineBreakMode="tailTruncation" numberOfLines="8" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qOc-gk-gSp">
<rect key="frame" x="16" y="76" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Placemark String" lineBreakMode="tailTruncation" numberOfLines="14" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="42A-nO-tNf">
<rect key="frame" x="16" y="104" width="343" height="20.5"/>
<color key="backgroundColor" red="0.80000001190000003" green="0.80000001190000003" blue="0.80000001190000003" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HgN-oB-ds0">
<rect key="frame" x="121.5" y="578" width="132" height="30"/>
<state key="normal" title="Request Placemark"/>
<connections>
<action selector="RefreshLocationButton:" destination="BYZ-38-t0r" eventType="touchUpInside" id="izY-S5-wjg"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="ZIg-u9-TMR" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="6HO-kN-l1z"/>
<constraint firstItem="qOc-gk-gSp" firstAttribute="top" secondItem="csF-KS-xGE" secondAttribute="bottom" constant="7.5" id="6ux-dx-gJr"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="42A-nO-tNf" secondAttribute="trailing" constant="16" id="8dG-gV-Cob"/>
<constraint firstItem="HgN-oB-ds0" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="A38-Nt-i3D"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="ZIg-u9-TMR" secondAttribute="trailing" constant="16" id="Bb0-YC-oov"/>
<constraint firstItem="csF-KS-xGE" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="FJU-ha-HkL"/>
<constraint firstItem="42A-nO-tNf" firstAttribute="top" secondItem="qOc-gk-gSp" secondAttribute="bottom" constant="7.5" id="SpR-XB-MLG"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="HgN-oB-ds0" secondAttribute="bottom" constant="59" id="Vr6-QE-230"/>
<constraint firstItem="42A-nO-tNf" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="aHo-kJ-aCb"/>
<constraint firstItem="csF-KS-xGE" firstAttribute="top" secondItem="ZIg-u9-TMR" secondAttribute="bottom" constant="7.5" id="c63-Mq-ItW"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="csF-KS-xGE" secondAttribute="trailing" constant="16" id="dlV-Hc-8XJ"/>
<constraint firstItem="qOc-gk-gSp" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="m5y-Q8-jxI"/>
<constraint firstItem="ZIg-u9-TMR" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="sdw-WQ-Bx9"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="qOc-gk-gSp" secondAttribute="trailing" constant="16" id="woB-ig-i5v"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="LocationCounterLabel" destination="ZIg-u9-TMR" id="K4g-WT-f82"/>
<outlet property="LocationLabel" destination="qOc-gk-gSp" id="Cdw-bw-EWt"/>
<outlet property="PlacemarkLabel" destination="42A-nO-tNf" id="dMh-bw-cRE"/>
<outlet property="RequestCounterLabel" destination="csF-KS-xGE" id="ai6-zn-Toi"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53.600000000000001" y="66.11694152923539"/>
</scene>
</scenes>
</document>