iOS Core Location 之 定位 与 地理编码

一、定位功能简介

要实现地图、导航功能,往往需要先熟悉定位功能。在 iOS 中通过 Core Location 框架进行定位操作。Core Location 自身可以单独使用,和地图开发框架 MapKit 完全是独立的。但是往往地图开发要配合定位框架使用。在 Core Location 中主要包含了定位、地理编码(包括反编码)功能。

二、CLLocationManager 主要方法和属性

要实现定位功能,需要了解 Core LoactionCLLocationManager 类,首先看一下这个类的一些主要方法和属性:

  1. 类方法
/// 是否启用定位服务,通常如果用户没有启用定位服务可以提示用户打开定位服务
class func locationServicesEnabled() -> Bool

///  定位服务授权状态,返回枚举类型
class func authorizationStatus() -> CLAuthorizationStatus
  1. 属性
/// 定位精度,枚举类型
var desiredAccuracy: CLLocationAccuracy 

/// 位置信息更新最小距离,只有移动大于这个距离才更新位置信息,默认为kCLDistanceFilterNone:不进行距离限制
var distanceFilter: CLLocationDistance

/// 位置更新可能会自动暂停,默认是会自动停止定位,建议设置为false
var pausesLocationUpdatesAutomatically: Bool
  1. 对象方法
/// 开始定位追踪,开始定位后将按照用户设置的更新频率执行locationManager(_:didUpdateLocations:)方法
func startUpdatingLocation()

/// 停止定位追踪
func stopUpdatingLocation()

/// 开始导航方向追踪
func startUpdatingHeading()

/// 停止导航方向追踪
func stopUpdatingHeading()

/// 开始对某个区域进行定位追踪,如果用户进入或者走出某个区域会调用代理方法反馈相关信息
func startMonitoring(for region: CLRegion)

/// 停止对某个区域进行定位追踪
func stopMonitoring(for region: CLRegion)

/// 请求应用使用时的定位服务授权,注意使用此方法前在要在info.plist中配置NSLocationWhenInUseUsageDescription
func requestWhenInUseAuthorization()

/// 请求应用的定位服务授权,注意使用此方法前在要在info.plist中配置NSLocationAlwaysUsageDescription
func requestAlwaysAuthorization()

三、后台定位的配置

如果希望应用程序在后台或者锁屏情况下还能使用定位功能,则必须在TARGETSCapabilities选项中打开Background Modes并勾选Location updates

勾选Location updates

四、定位功能实现

import CoreLocation

typealias LocationResult = (_ location: CLLocation?, _ status: CLAuthorizationStatus, _ error: Error?) -> ()

class LocationManager: NSObject {

    /// 单例
    static let shared = LocationManager()
    
    fileprivate override init() { super.init() }
    
    /// 是否只定位一次
    fileprivate var atOnce: Bool = false
    
    /// 定位结果回调
    fileprivate var resultBlock: LocationResult?
    
    /// 定位授权状态
    fileprivate var authorizationStatus: CLAuthorizationStatus = .notDetermined
    
    /// 定位管理者
    fileprivate lazy var locationManager: CLLocationManager = {
       
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.pausesLocationUpdatesAutomatically = false
        
        // 请求授权
        if #available(iOS 8.0, *) {
            
            guard let infoDic = Bundle.main.infoDictionary else { return locationManager }
            
            let whenInUse = infoDic["NSLocationWhenInUseUsageDescription"]
            let always = infoDic["NSLocationAlwaysAndWhenInUseUsageDescription"]
            
            if let backgroundModes = infoDic["UIBackgroundModes"] as? [String], backgroundModes.contains("location"), #available(iOS 9.0, *) {
                
                locationManager.allowsBackgroundLocationUpdates = true
            }
            
            if always != nil {
                locationManager.requestAlwaysAuthorization()
            } else if whenInUse != nil {
                locationManager.requestWhenInUseAuthorization()
            } else {
                print("错误提示:在 iOS8.0 以后,想要使用用户位置,要主动请求授权。应该在 info.plist 配置 NSLocationWhenInUseUsageDescription 或者 NSLocationAlwaysAndWhenInUseUsageDescription")
            }
        }
        return locationManager
    }()
    
    /// 获取当前位置
    ///
    /// - Parameters:
    ///   - atOnce: 是否只定位一次,若为 true,则定位一次后停止定位
    ///   - resultBlock: 位置信息
    func startUpdatingLocation(atOnce: Bool, resultBlock: @escaping LocationResult) -> () {
        
        self.atOnce = atOnce
        self.resultBlock = resultBlock
        
        if CLLocationManager.locationServicesEnabled() {
            locationManager.startMonitoringSignificantLocationChanges()
            locationManager.startUpdatingLocation()
        }else {
            self.resultBlock?(nil, CLAuthorizationStatus.denied, nil)
        }
    }
    
    /// 停止定位
    func stopUpdatingLocation() {
        locationManager.stopUpdatingLocation()
        locationManager.stopMonitoringSignificantLocationChanges()
    }
}

// MARK: - CLLocationManagerDelegate
extension LocationManager: CLLocationManagerDelegate {

    /// 定位发生错误
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        resultBlock?(nil, authorizationStatus, error)
    }

    /// 定位信息改变
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        guard locations.count > 0, let location = locations.last else {
            resultBlock?(nil, authorizationStatus, nil)
            return
        }
        resultBlock?(location, authorizationStatus, nil)
        if atOnce { manager.stopUpdatingLocation() }
    }

    /// 定位授权状态改变
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        
        authorizationStatus = status
        locationManager(locationManager, didUpdateLocations: [])
    }
}

五、地理编码

除了提供位置跟踪功能之外,在定位服务中还包含 CLGeocoder 类,用于处理地理编码和逆地理编码(又叫反地理编码)功能。

  • 地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。
  • 反地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。

CLGeocoder 最主要的两个方法就是:

/// 地理编码
func geocodeAddressString(_ addressString: String, completionHandler: @escaping CoreLocation.CLGeocodeCompletionHandler)

/// 反地理编码
func reverseGeocodeLocation(_ location: CLLocation, completionHandler: @escaping CoreLocation.CLGeocodeCompletionHandler)

具体使用方法如下:

  1. 输入地名,获取 经纬度 和 当地天气
geocoder.geocodeAddressString("广州") { (placemarks, error) in
            
    guard let placemarkName = placemarks?.first.name else { return }
    
    // OpenWeatherMap——查询天气接口API
    let urlString = "https://api.openweathermap.org/data/2.5/weather?q=\(placemarkName)&appid=594a923fe158c2c45c54aca585f2707b"

    guard let url = URL(string: urlString) else { return }
            
    let sessionDataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data, let json = try? JSONSerialization.jsonObject(with: data) else { return }
        print(json)
    }
    sessionDataTask.resume()
}
  1. 输入经纬度,获取城市信息
let location = CLLocation(latitude: 23.125178, longitude: 113.280637)
        
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
            
    guard let placemark = placemarks?.first else { return }
    print(placemark.addressDictionary?["City"])
 }

注意:以上两个地理编码方法,需要在有网的时候才能获取结果,其实质就是去网络获取信息

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容