老早就想總結一篇地圖定位相關的文章,今天終于有空,有出錯的地方歡迎指正。
一.CoreLocation的三大功能:
1.地理定位:定位用戶所在的位置, 獲取對應的經(jīng)緯度或者海拔等信息
2.地理編碼:分地理方法和反地理編碼
地理編碼: 北京市 北京市區(qū)------》39.9,116. 3
反地理編碼: 39.9,116. 3------ 》北京市 北京市區(qū)
3.區(qū)域監(jiān)聽:事先在APP內部通過代碼,指定一個區(qū)域, 那么當用戶進入或離開區(qū)域的時候, 我們都可以監(jiān)聽到
定位用戶所在的位置, 獲取對應的經(jīng)緯度或者海拔等信息
二.地理定位
<一>定位授權
iOS8.0之后,想要獲取用戶的位置信息,需要主動請求授權(前臺定位授權or前后臺定位授權)
申請前臺定位授權:###
1.必須在info.plist文件中配置對應的key NSLocationWhenInUseUsageDescription
2.默認情況下只能在前臺獲取用戶的位置信息(用戶進入軟件才能獲取位置信息)
3.如果想要在后臺也能獲取用戶的位置信息,需要:開啟后臺模式 location updates
4.在后臺獲取用戶的位置信息,系統(tǒng)會在頂部顯示藍色橫幅,時時提示用戶該app在獲取你的位置信息,點擊藍色橫幅則可以打開該app(這個是不好的,小心用戶卸了你的APP)
locationM.requestWhenInUseAuthorization()// 前臺定位授權
申請前后臺定位授權:###
1.不會出現(xiàn)藍色橫幅
2.必須在info.plist文件中配置對應的key NSLocationAlwaysUsageDescription
3.在前臺和后臺都能夠獲取用戶的位置信息,在后臺獲取用戶的位置信息,不需要開啟后臺模式
locationM.requestAlwaysAuthorization()// 前后臺定位授權
iOS9.0之后
申請前臺定位授權:
(跟iOS8.0一樣,只是在前臺授權中,想在后臺獲取用戶位置時多了如下條件)
1.需要開啟后臺模式: location updates
2.必須允許后臺獲取用戶的位置信息(下面的版本適配代碼)
3.在后臺獲取用戶的位置信息,也會出現(xiàn)iOS8.0的那個藍色橫幅提示信息
注意點: 如果允許后臺獲取用戶的位置信息,必須勾選后臺模式,否則此代碼會造成崩潰
if #available(iOS 9.0, *) {
locationM.allowsBackgroundLocationUpdates = true
}
申請前后臺定位授權:相對于iOS8.0,沒有什么更新的東西
注意:如果是做監(jiān)聽區(qū)域,使用的授權必須是前后臺定位授權
<二>.定位是使用CoreLocation這個框架實現(xiàn)的,而跟定位離不開關系的是位置管理者 CLLocationManager,使用位置管理者的時候,通常會提供一個懶加載的方法來保證他不被銷毀.蘋果給他提供了代理的接口,外界可以通過設置代理來獲取他所能管理的所有東西和動態(tài).下面介紹一些地理定位時常用的代理方法:
/// 代理方法一:當獲取到用戶的位置的時候會來到該方法
/// - Parameters:
/// - manager: 位置管理者
/// - locations: 位置數(shù)組
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//locations是一個保存著CLLocation對象的集合,我們一般都是取出last,因為他是最新的.
print("定位到了")
}
/// 代理方法二:當授權狀態(tài)發(fā)生改變的時候會來到該方法(授權狀態(tài)不受開發(fā)人員控制,由用戶控制)
/// - Parameters:
/// - manager: 位置管理者
/// - status: 授權狀態(tài)( notDetermined, denied, restricted, authorizedAlways, authorizedWhenInUse)
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {}
///代理方法三:定位失敗的時候會來到該方法
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("定位失敗")
}
注意:當用戶自己拒絕APP對他進行位置定位的時或手機主人之前已經(jīng)把手機的定位功能關閉時,status都是拒絕狀態(tài),如果手機的定位功能處于關閉狀態(tài),蘋果會自動跳轉到手機設置界面,但是如果是用戶自己在打開APP時直接選擇拒絕的,蘋果不會自動跳轉手機設置界面,需要開發(fā)者做友情提示用戶自己去開啟.
<三>.給位置管理者CLLocationManager設置一些條件來進行定位(以下出現(xiàn)locationM的地方都是代表CLLocationManager的一個實例對象)
1.locationM.distanceFilter = 100//相對于上一次位置,大于100米就獲取一次用戶的位置信息
2. locationM.desiredAccuracy = kCLLocationAccuracyBest//想要的精確度
//AccuracyBestForNavigation : 最適合導航
//AccuracyBest : 最好的
//AccuracyNearestTenMeters : 附近10米
//AccuracyHundredMeters : 附近100米
//AccuracyKilometer : 附近1000米
//AccuracyThreeKilometers : 附近3000米
注意:精確度越高,越耗電,定位時間越久
<四>.啟動監(jiān)聽的方法
1.locationM.startUpdatingLocation()
//啟動監(jiān)聽用戶位置的方法,該方法會不斷獲取用戶位置信息,如果只想獲取一次位置信息,可以在代理方法一中停止監(jiān)聽 manager.stopUpdatingLocation()
// startUpdatingLocation()方法被稱為標準定位服務(基于GGPS/wifi/藍牙/蜂窩基站)
// GPS:定位最準確,缺點:如果被建筑物擋住,就檢測不到了
// wifi:不能使用則GPS使用wifi
// 蜂窩基站: 不能使用wifi,則使用蜂窩基站
// 藍牙 : iwatch藍牙連接手機
// 優(yōu)點: 定位精確度高
// 缺點: 比較耗電,APP必須運行著才能獲取用戶的位置信息
// 具體使用什么方式進行定位由蘋果決定
2.locationM.startMonitoringSignificantLocationChanges()
// 監(jiān)聽重大位置改變(基于基站定位,意思是要有電話卡才能監(jiān)聽)
// 優(yōu)點: 比較省電,z只要APP還安裝在用戶手機上,都可以獲取用戶的位置信息,當用戶的位置發(fā)生改變的時候,會自動啟動該app在后臺執(zhí)行,來獲取用戶的位置信息
// 缺點: 定位精確度低,硬性要求必須有電話模塊
<五>.CLLocation的詳細解釋 (經(jīng)常在代理方法中返回的對象)
CLLocation : 位置對象,他里面可以拿到的信息有很多,下面只是了解其中的小部分
// coordinate : 經(jīng)緯度信息
// altitude : 海拔
// horizontalAccuracy : 水平精確度,如果為負數(shù),代表位置不可用,
// verticalAccuracy : 垂直精確度,如果為負數(shù),代表海拔不可用
// course : 航向,如果為負數(shù),代表航向不可用
// speed : 速度,如果為負數(shù),代表速度不可用
// timestamp : 獲取當前定位到的時間
// distance(from location: CLLocation) : 計算2個位置之間的實際物理距離
//打印這個對象獲得的信息: <+38.78583400(緯度),-122.40641700(經(jīng)度)> +/- 5.00m(水平精確度) (speed(速度) -1.00 mps / course(航向) -1.00) @ 11/11/16, 2:48:45 PM China Standard Time(獲取當前位置的時間)
// 可以返回負數(shù)的那些參數(shù),利用他們篩選數(shù)據(jù),判斷哪些信息需要上傳后臺
三. 地理編碼
@IBOut let weak var addressTV: UITextView!
@IBOut let weak var latitudeTF: UITextField!
@IBOut let weak var longitudeTF: UITextField!
/// 必須聯(lián)網(wǎng)!!!
lazy var geoc : CLGeocoder = {
return CLGeocoder()//CLGeocoder地理編碼要使用的類
}()
//監(jiān)聽地理編碼的點擊
@IBAction func geocClick() {
let address = addressTV.text!
if address.characters.count == 0 {return}
/// 地理編碼的方法,閉包參考參數(shù)public typealias CLGeocodeCompletionHandler = ([CLPlacemark]?, Error?) -> Swift.Void() swift3.0后閉包敲回車不管用了,這個方法可以知道參數(shù)是什么
geoc.geocodeAddressString(address, completionHandler: {(clpls : [CLPlacemark]?,error : Error? )in
[CLPlacemark] : 地標對象的數(shù)組,
//地標對象可以拿到的信息: location : 用戶的位置(經(jīng)緯度信息)// name : 地址// locality : 城市// country : 國家
if error != nil {
return
}
// 1.獲取地標對象(第一個相關度是最高的)
guard let clpl = clpls?.first else {return}
// 2.設置地址
self.addressTV.text = "國家:\\(clpl.country ?? "")\\n 城市:\\(clpl.locality ?? "")\\n 地址:\\(clpl.name ?? "")"
// 3.設置latitudeTF該顯示的經(jīng)緯度
self.latitudeTF.text = clpl.location?.coordinate.latitude.description ?? ""
self.longitudeTF.text = clpl.location?.coordinate.longitude.description ?? ""
})
}
//監(jiān)聽反地理編碼方法的點擊
@IBAction func reverseGeocClick() {
let latitude = CLLocationDegrees(latitudeTF.text!) ?? 0
let longitude = CLLocationDegrees(longitudeTF.text!) ?? 0
let location = CLLocation(latitude: latitude, longitude: longitude)
/// 反地理編碼的方法
geoc.reverseGeocodeLocation(location, completionHandler: {(clpls : [CLPlacemark]?,error : Error? )in
if error != nil {return}
// 1.獲取地標對象(第一個相關度是最高的)
guard let clpl = clpls?.first else {return}
// 2.設置地址
self.addressTV.text = "國家:\\(clpl.country ?? "")\\n 城市:\\(clpl.locality ?? "")\\n 地址:\\(clpl.name ?? "")"
// 3.設置經(jīng)緯度
self.latitudeTF.text = clpl.location?.coordinate.latitude.description ?? ""
self.longitudeTF.text = clpl.location?.coordinate.longitude.description ?? ""
})
}
四.區(qū)域監(jiān)聽
<一>開啟區(qū)域監(jiān)聽
override func viewDidLoad() {
super.viewDidLoad()
//設置監(jiān)聽區(qū)域后開啟監(jiān)聽
// center : 經(jīng)緯度(區(qū)域中心點)
// radius : 區(qū)域的半徑
// identifier : 區(qū)域的標識
let center : CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 12.123, longitude: 123.456)
let radius : CLLocationDistance = 1000
let region = CLCircularRegion(center: center, radius: radius, identifier: "廣州")
locationM.startMonitoring(for: region)
// 主動請求用戶的位置對于這個區(qū)域的狀態(tài),用來提醒用戶自己目前的狀態(tài)
// 一個區(qū)域被監(jiān)聽之后,才能去請求這個區(qū)域的狀態(tài)
// 當一個區(qū)域能被監(jiān)聽之后(didStartMonitoringFor),會把該區(qū)域放入集合:monitoredRegions
// 這個集合最多能存儲20個區(qū)域,集合不能重復
if locationM.monitoredRegions.first != nil {//判斷集合中有沒有保存過 監(jiān)聽區(qū)域
locationM.requestState(for: region)
}
}
<二>區(qū)域監(jiān)聽的相關代理方法講解
/// 當這個區(qū)域開始被監(jiān)聽的時候,就會來到didStartMonitoringFor方法
/// 能夠來到這里,說明這個區(qū)域是可以被監(jiān)聽的
/// - Parameters:
/// - manager: 位置管理者
/// - region: 區(qū)域
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("開始監(jiān)聽這個區(qū)域 -> \\(region.identifier)")
}
/// 進入該區(qū)域的時候會來到didEnterRegion方法(動作)
/// - Parameters:
/// - manager: 位置管理者
/// - region: 區(qū)域
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("進入該區(qū)域 -> \\(region.identifier)")
}
/// 離開該區(qū)域的時候會來到didExitRegion方法(動作)
/// - Parameters:
/// - manager: 位置管理者
/// - region: 區(qū)域
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("離開該區(qū)域 -> \\(region.identifier)")
}
/// 當這個區(qū)域不能夠被監(jiān)聽時候,就會來到monitoringDidFailFor region方法
/// 為什么不能被監(jiān)聽?: 監(jiān)聽的區(qū)域超過20個/寫的區(qū)域半徑太大,經(jīng)緯度信息不對
/// - Parameters:
/// - manager: 位置管理者
/// - region: 區(qū)域
/// - error: 錯誤信息
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
// 如果添加滿了20個,則會來到該代理方法中
// 在這里一般移除與用戶當前位置最遠的那個區(qū)域
// 移除掉之后就可以添加新的了
print("此區(qū)域無法被監(jiān)聽 -> \\(region?.identifier)")
}
/// 當請求區(qū)域狀態(tài)的時候,會來到didDetermineState方法
/// 如果用戶對于這個區(qū)域的狀態(tài)發(fā)生改變也會來到該方法(動作)
/// - Parameters:
/// - manager: 位置管理者
/// - state: 狀態(tài)
/// - region: 區(qū)域
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
switch state {
case .inside:
print("進入?yún)^(qū)域狀態(tài) -> \\(region.identifier)")
case .outside:
print("離開區(qū)域狀態(tài) -> \\(region.identifier)")
case .unknown:
print("沒有獲取用戶的位置")
}
}
the end, it's hard to write an article, hope this can help you a little bit. Thanks for reading.