CoreLocation 是 iOS 中用於設備定位的框架。經過這個框架能夠實現定位進而獲取位置信息如經度、緯度、海拔信息等。git
CoreLocation
模塊中,使用時必須導入。CLLocationManager
:定位管理器,能夠理解爲定位不能本身工做,須要有個類對它進行全過程管理。CLLocationManagerDelegate
:定位管理代理,不論是定位成功與失敗,都會有相應的代理方法進行回調。CLLocation
:表示某個位置的地理信息,包含經緯度、海拔等。CLPlacemark
:位置信息,包含的信息如國家、城市、街道等。CLGeocoder
:地理編碼。CLLocationManager
,設置代理併發起定位。CLLocationManagerDelegate
中定位成功和失敗的代理方法。CLLocation
對象並經過CLGeocoder
進行反向地理編碼獲取對應的位置信息CLPlacemark
。CLPlacemark
獲取具體的位置信息。requestWhenInUseAuthorization
發起定位受權。requestAlwaysAuthorization
發起定位受權。Privacy - Location When In Use Usage Description
。requestWhenInUseAuthorization
發起定位受權。locationManager.allowsBackgroundLocationUpdates = true
。(1)Privacy - Location When In Use Usage Description
+ requestWhenInUseAuthorization
:能夠後臺定位,但會在設備頂部出現藍條(劉海屏設備會出如今左邊劉海)。 (2)Privacy - Location When In Use Usage Description
+ Privacy - Location Always and When In Use Usage Description
+ requestAlwaysAuthorization
:能夠後臺定位,不會出現藍條。這種方式會出現 2 次受權對話框:第一次和前臺定位同樣,在贊成使用While Using App
模式後,繼續使用定位纔會彈出第二次,詢問是否切換到Always
模式。swift
CLLocationManager
的accuracyAuthorization
屬性獲取當前的定位精度權限。CLLocationManager
的requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String, completion: ((Error?) -> Void)? = nil)
方法申請一次臨時精肯定位權限,其中purposeKey
爲 Info.plist 中配置的Privacy - Location Temporary Usage Description Dictionary
字段下某個具體緣由的 key,能夠設置多個 key 以應對不一樣的定位使用場景。requestTemporaryFullAccuracyAuthorization
方法並不能用於申請定位權限,只能用於從模糊定位升級爲精肯定位;若是沒有得到定位權限,直接調用此 API 無效。Privacy - Location Default Accuracy Reduced
爲YES
,此時申請定位權限的小地圖中再也不有精度切換開關。須要注意 2 點:(1)若是發現該字段不是 Bool 型,須要以源碼形式打開 Info.plist,而後手動修改<key>NSLocationDefaultAccuracyReduced</key>
爲 Bool 型的值,不然沒法生效。 (2)配置該字段後,若是 Info.plist 中還配置了Privacy - Location Temporary Usage Description Dictionary
,則仍能夠經過requestTemporaryFullAccuracyAuthorization
申請臨時的精肯定位權限,會再次彈出受權對話框進行確認。markdown
因爲定位須要 GPS,通常狀況下須要真機進行測試。但對於模擬器,也能夠進行虛擬定位,主要有 3 種方式。併發
(1)新建一個gpx
文件,能夠取名XXX.gpx
,而後將本身的定位信息填寫進 xml 對應的位置。 (2)gpx
文件設置完成之後,首先須要運行一次 App,而後選擇Edit Scheme
,在Options
中選擇本身的gpx
文件,這樣模擬器運行的時候就會讀取該文件的位置信息。而後能夠選擇Debug
—>Simulate Location
或底部調試欄上的定位按鈕進行gpx
文件或位置信息的切換。框架
<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
<!--安徽商貿職業技術學院 谷歌地球:31.2906511800,118.3623587000-->
<wpt lat="31.2906511800" lon="118.3623587000">
<name>安徽商貿職業技術學院</name>
<cmt>中國安徽省蕪湖市弋江區文昌西路24號 郵政編碼: 241002</cmt>
<desc>中國安徽省蕪湖市弋江區文昌西路24號 郵政編碼: 241002</desc>
</wpt>
</gpx>
複製代碼
CoreLocation
模塊。CLLcationManager
對象,設置參數和代理,配置 Info.plist 並請求定位受權。CLLcationManager
對象的startUpdatingLocation()
或requestLocation()
方法進行定位。import CoreLocation
import UIKit
class ViewController: UIViewController {
// CLLocationManager
lazy var locationManager = CLLocationManager()
// CLGeocoder
lazy var gecoder = CLGeocoder()
override func viewDidLoad() {
super.viewDidLoad()
setupManager()
}
func setupManager() {
// 默認狀況下每當位置改變時LocationManager就調用一次代理。經過設置distanceFilter能夠實現當位置改變超出必定範圍時LocationManager才調用相應的代理方法。這樣能夠達到省電的目的。
locationManager.distanceFilter = 300
// 精度 好比爲10 就會盡可能達到10米之內的精度
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// 代理
locationManager.delegate = self
// 第一種:能後臺定位可是會在頂部出現大藍條(打開後臺定位的開關)
// 容許後臺定位
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestWhenInUseAuthorization()
// 第二種:能後臺定位而且不會出現大藍條
// locationManager.requestAlwaysAuthorization()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 如下2個方法都會調用代理方法
// 1. 發起位置更新(定位)會一直輪詢,耗電
locationManager.startUpdatingLocation()
// 2. 只請求一次用戶的位置,省電
// locationManager.requestLocation()
}
}
extension ViewController: CLLocationManagerDelegate {
// 定位成功
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
// 反地理編碼轉換成具體的地址
gecoder.reverseGeocodeLocation(location) { placeMarks, _ in
// CLPlacemark -- 國家 城市 街道
if let placeMark = placeMarks?.first {
print(placeMark)
// print("\(placeMark.country!) -- \(placeMark.name!) -- \(placeMark.locality!)")
}
}
}
// 中止位置更新
locationManager.stopUpdatingLocation()
}
// 定位失敗
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
複製代碼
MapKit
模塊中,使用時必須導入。顯示地圖,同時顯示用戶所處的位置。點擊用戶的位置,顯示一個氣泡展現用戶位置的具體信息。dom
import MapKit
class ViewController: UIViewController {
@IBOutlet var mapView: MKMapView!
lazy var locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
}
func setupManager() {
locationManager.requestWhenInUseAuthorization()
// 不須要發起定位
}
func setupMapView() {
// 設置定位
setupManager()
// 地圖類型
mapView.mapType = .hybridFlyover
// 顯示興趣點
mapView.showsPointsOfInterest = true
// 顯示指南針
mapView.showsCompass = true
// 顯示交通
mapView.showsTraffic = true
// 顯示建築
mapView.showsBuildings = true
// 顯示級別
mapView.showsScale = true
// 用戶跟蹤模式
mapView.userTrackingMode = .followWithHeading
}
}
複製代碼
在以前功能的基礎上實現地圖的任意視角(「縮放級別」)。ide
// 設置「縮放級別」
func setRegion() {
if let location = location {
// 設置範圍,顯示地圖的哪一部分以及顯示的範圍大小
let region = MKCoordinateRegion(center: mapView.userLocation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500)
// 調整範圍
let adjustedRegion = mapView.regionThatFits(region)
// 地圖顯示範圍
mapView.setRegion(adjustedRegion, animated: true)
}
}
複製代碼
在地圖上能夠添加標註來顯示一個個關鍵的信息點,用於對用戶的提示。學習
class MapFlag: NSObject, MKAnnotation {
// 標題
let title: String?
// 副標題
let subtitle: String?
// 經緯度
let coordinate: CLLocationCoordinate2D
// 附加信息
let urlString: String
init(title: String?, subtitle: String?, coordinate: CLLocationCoordinate2D, urlString: String) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.urlString = urlString
}
}
複製代碼
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let flag = MapFlag(title: "標題", subtitle: "副標題", coordinate: CLLocationCoordinate2D(latitude: 31.2906511800, longitude: 118.3623587000), urlString: "https://www.baidu.com")
mapView.addAnnotation(flag)
}
複製代碼
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? MapFlag else {
return nil
}
// 若是是用戶的位置,使用默認樣式
if annotation == mapView.userLocation {
return nil
}
// 標註的標識符
let identifier = "marker"
// 獲取AnnotationView
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
// 判空
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
// 顯示氣泡
annotationView?.canShowCallout = true
// 左邊顯示的輔助視圖
annotationView?.leftCalloutAccessoryView = UIImageView(image: UIImage(systemName: "heart"))
// 右邊顯示的輔助視圖
let button = UIButton(type: .detailDisclosure, primaryAction: UIAction(handler: { _ in
print(annotation.urlString)
}))
annotationView?.rightCalloutAccessoryView = button
}
return annotationView
}
}
複製代碼
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? MapFlag else {
return nil
}
// 若是是用戶的位置,使用默認樣式
if annotation == mapView.userLocation {
return nil
}
// 標註的標識符
let identifier = "custom"
// 標註的自定義圖片
let annotationImage = ["pin.circle.fill", "car.circle.fill", "airplane.circle.fill", "cross.circle.fill"]
// 獲取AnnotationView
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
// 判空
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
// 圖標,每次隨機取一個
annotationView?.image = UIImage(systemName: annotationImage.randomElement()!)
// 顯示氣泡
annotationView?.canShowCallout = true
// 左邊顯示的輔助視圖
annotationView?.leftCalloutAccessoryView = UIImageView(image: UIImage(systemName: "heart"))
// 右邊顯示的輔助視圖
let button = UIButton(type: .detailDisclosure, primaryAction: UIAction(handler: { _ in
print(annotation.urlString)
}))
annotationView?.rightCalloutAccessoryView = button
// 彈出的位置偏移
annotationView?.calloutOffset = CGPoint(x: -5.0, y: 5.0)
}
return annotationView
}
}
// 點擊地圖插入一個標註,標註的標題和副標題顯示的是標註的具體位置
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchPoint = touches.first?.location(in: mapView)
// 將座標轉換成爲經緯度,而後賦值給標註
let coordinate = mapView.convert(touchPoint!, toCoordinateFrom: mapView)
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
let gecoder = CLGeocoder()
// 反地理編碼轉換成具體的地址
gecoder.reverseGeocodeLocation(location) { placeMarks, _ in
let placeMark = placeMarks?.first
if let placeMark = placeMark {
let flag = MapFlag(title: placeMark.locality, subtitle: placeMark.subLocality, coordinate: coordinate, urlString: "https://www.baidu.com")
self.mapView.addAnnotation(flag)
}
}
}
複製代碼