iOS Swift 使用 CLLocationManager 定位
CLLocationManager 是IOS 系统提供的定位对象,通过该对象可以获取定位信息,包括:经纬度、海拔、方向、速度。通过反向编码可以获取更多详细的地理区域信息:国家(国家码:ex:美国 US)、省份、市县以及具体的街道信息、周边感兴趣地点等。
具体使用步骤如下:
1、导入相关文件
导入 CoreLocation.framework 到 项目中,并使用 #import 引入
2、plist 文件配置
在Info.plist文件中添加如下配置:
NSLocationAlwaysUsageDescription(或者 NSLocationWhenInUseUsageDescription)
3、具体使用及参考代码如下:
import UIKit
import SVProgressHUD
//定位
import CoreLocation
/// App 定位类
class AppLocationData : NSObject {
static let shareInstance = AppLocationData.init()
/// 获取最新定位信息
var getLocationInfo:((_ _country:String?,_ _province:String?,_ _city:String?,_ _county:String?,_ _address:String?)->Void)?
//初始化
override init() {
super.init()
self.locationInit()
}
deinit {
self.llocationManager?.stopUpdatingLocation()
self.llocationManager?.delegate = nil
self.llocationManager = nil
print("\(self.className()) 已销毁")
}
//MARK: - lazy load
/// 定位对象
private lazy var llocationManager:CLLocationManager? = nil
}
//MARK: -
extension AppLocationData {
/// 定位初始化
private func locationInit(){
//定位对象
self.llocationManager = CLLocationManager.init()
weak var weakSelf = self
self.llocationManager?.delegate = weakSelf
/**
设置精度
kCLLocationAccuracyBest 精确度最佳
kCLLocationAccuracyNearestTenMeters 精确度10m以内
kCLLocationAccuracyHundredMeters 精确度100m以内
kCLLocationAccuracyKilometer 精确度1000m以内
kCLLocationAccuracyThreeKilometers 精确度3000m以内
*/
self.llocationManager?.desiredAccuracy = kCLLocationAccuracyBest
//设置间隔距离(单位:m) 内更新定位信息
//定位要求的精度越高,distanceFilter属性的值越小,应用程序的耗电量就越大。
self.llocationManager?.distanceFilter = 200.0
}
/// 权限检测
private func locationPermissionsCheck(){
if CLLocationManager.locationServicesEnabled() == false {
SVProgressHUD.showInfo(withStatus: "请确认已开启定位服务")
Utils.shareInstance().gotoSettingFor(Title: "定位服务", andViewController: nil)
return
}
// 请求用户授权
if CLLocationManager.authorizationStatus() == .notDetermined {
self.llocationManager?.requestWhenInUseAuthorization()
}
SVProgressHUD.dismiss()
}
/// 反编码获取地址信息
private func getGeocoderInfoFor(Location location:CLLocation){
// 如果断网或者定位失败
if OtherModule.isNetWork() == false {
print("没有网络无法反编码地址")
SVProgressHUD.showInfo(withStatus: "开启网络后重试")
return
}
let geoCoder = CLGeocoder.init()
geoCoder.reverseGeocodeLocation(location) {[weak self] (_placemarks:[CLPlacemark]?, _error:Error?) in
guard let self = self else { return }
guard let placemarks = _placemarks,placemarks.count > 0 else {
SVProgressHUD.showInfo(withStatus: "获取地理信息失败,请重试")
return
}
/*
* region: //地理区域
* addressDictionary:[AnyHashable : Any] //可以使用ABCreateStringWithAddressDictionary格式化为一个地址
* thoroughfare: String? //街道名
* name:String? //地址
* subThoroughfare: String? //大道
* locality: String? //城市
* subLocality: String? // 社区,通用名称
* administrativeArea: String? // state, eg. CA
* subAdministrativeArea: String? // 国家, eg. Santa Clara
* postalCode: String? // zip code, eg. 95014
* isoCountryCode: String? // eg. US
* country: String? // eg. United States
* inlandWater: String? // 湖泊
* ocean: String? // 洋
* areasOfInterest: [String]? // 感兴趣的地方
*/
let placeMark:CLPlacemark = placemarks[0]
print("定位信息:\(String(describing: placeMark.addressDictionary))")
//当前城市
let strCurrentCity = placeMark.locality
//详细地址
var strCurrentAddress = placeMark.addressDictionary?["FormattedAddressLines"] as? String
if strCurrentAddress == nil {
strCurrentAddress = placeMark.addressDictionary?["Street"] as? String
}
//国家
let strCurrentCountry = placeMark.addressDictionary?["Country"] as? String
//省份(直辖市时,省份没有)
var strCurrentProvince = placeMark.addressDictionary?["State"] as? String
if strCurrentProvince == nil {
strCurrentProvince = placeMark.locality
}
//区(县)
let strCurrentArea = placeMark.addressDictionary?["SubLocality"] as? String
//闭包回调
self.getLocationInfo?(
strCurrentCountry,
strCurrentProvince,
strCurrentCity,
strCurrentArea,
strCurrentAddress
)
//关闭定位
self.llocationManager?.stopUpdatingLocation()
SVProgressHUD.dismiss()
}
}
/// 开启定位
func startLoaction(){
SVProgressHUD.show(withStatus: "定位中...")
//已开启定位服务
if CLLocationManager.locationServicesEnabled() {
//是否有授权本App获取定位
var _auth:CLAuthorizationStatus?
if #available(iOS 14.0, *) {
_auth = self.llocationManager?.authorizationStatus
} else {
// Fallback on earlier versions
_auth = CLLocationManager.authorizationStatus()
}
if _auth == nil || _auth == .denied || _auth == .restricted {
SVProgressHUD.dismiss()
Utils.shareInstance().gotoSettingFor(Title: "授权访问定位", andViewController: nil)
return
}
//可以定位
self.llocationManager?.startUpdatingLocation()
}
else{
self.locationPermissionsCheck()
}
}
}
//MARK: - CLLocationManagerDelegate
extension AppLocationData : CLLocationManagerDelegate {
/// 定位失败
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("定位失败:\(error.localizedDescription)")
SVProgressHUD.showInfo(withStatus: error.localizedDescription)
}
/// 定位成功
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("定位成功")
/**
* CLLocation 定位信息
*
* 经度:currLocation.coordinate.longitude
* 纬度:currLocation.coordinate.latitude
* 海拔:currLocation.altitude
* 方向:currLocation.course
* 速度:currLocation.speed
* ……
*/
if let _location = locations.last {
self.getGeocoderInfoFor(Location: _location)
}
else{
SVProgressHUD.showInfo(withStatus: "请稍后重试")
}
}
}
距离计算: CCLocation对象的distanceTo(distanceFrom)方法,可以得到两个坐标间的距离,单位是米,由于 使用的坐标标准不一致 CLLocationManager 在 百度、腾讯、高德等地图上显示会有 偏差,具体原因及分析 和 解决办法见下文:
IOS LocationManager定位国内偏移,火星坐标(GCJ-02)解决方法