一、定位功能简介
要实现地图、导航功能,往往需要先熟悉定位功能。在 iOS 中通过 Core Location 框架进行定位操作。Core Location 自身可以单独使用,和地图开发框架 MapKit 完全是独立的。但是往往地图开发要配合定位框架使用。在 Core Location 中主要包含了定位、地理编码(包括反编码)功能。
二、CLLocationManager 主要方法和属性
要实现定位功能,需要了解 Core Loaction 中 CLLocationManager 类,首先看一下这个类的一些主要方法和属性:
- 类方法
1 2 3 4 5
| /// 是否启用定位服务,通常如果用户没有启用定位服务可以提示用户打开定位服务 class func locationServicesEnabled() -> Bool
/// 定位服务授权状态,返回枚举类型 class func authorizationStatus() -> CLAuthorizationStatus
|
- 属性
1 2 3 4 5 6 7 8
| var desiredAccuracy: CLLocationAccuracy
var distanceFilter: CLLocationDistance
var pausesLocationUpdatesAutomatically: Bool
|
- 对象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func startUpdatingLocation()
func stopUpdatingLocation()
func startUpdatingHeading()
func stopUpdatingHeading()
func startMonitoring(for region: CLRegion)
func stopMonitoring(for region: CLRegion)
func requestWhenInUseAuthorization()
func requestAlwaysAuthorization()
|
三、后台定位的配置
如果希望应用程序在后台或者锁屏情况下还能使用定位功能,则必须在TARGETS
的Capabilities
选项中打开Background Modes
并勾选Location updates
:
四、定位功能实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| 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["NSLocationAlwaysUsageDescription"] 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 或者 NSLocationAlwaysUsageDescription") } } return locationManager }() 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() } }
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 最主要的两个方法就是:
1 2 3 4 5
| func geocodeAddressString(_ addressString: String, completionHandler: @escaping CoreLocation.CLGeocodeCompletionHandler)
func reverseGeocodeLocation(_ location: CLLocation, completionHandler: @escaping CoreLocation.CLGeocodeCompletionHandler)
|
具体使用方法如下:
- 输入地名,获取 经纬度 和 当地天气
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| geocoder.geocodeAddressString("广州") { (placemarks, error) in guard let placemarkName = placemarks?.first.name else { return } 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 2 3 4 5 6 7
| 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"]) }
|
注意:以上两个地理编码方法,需要在有网的时候才能获取结果,其实质就是去网络获取信息