iOS Swift 使用 CLLocationManager 定位

訾旭
2023-12-01

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)解决方法

 类似资料: