iOS 12 swift 开发(二)之定位

定位类CoreLocation简介

Core Location provides services for determining a device’s geographic location, altitude, orientation, or position relative to a nearby iBeacon. The framework uses all available onboard hardware, including Wi-Fi, GPS, Bluetooth, magnetometer, barometer, and cellular hardware to gather data.

CoreLocation这个类提供了设备所处的地理位置,海拔,方向,或者相对于附近的标志物的相对位置的信息。这个框架使用了所有可用的硬件,包括Wi-Fi,GPS,蓝牙,磁力计,气压计和蜂窝数据网络硬件来收集信息。

定位授权类型

定位的授权分为两种类型

  • when-in-use authorization: 可以在应用运行期间使用定位服务,可以开启后台运行时扔使用定位,但是不能在获取定位时自动启动应用
  • always authorization: 可以使用所有的定位服务,如果与定位相关的事件发生时应用没有在运行,会自动启动你的应用并发送定位事件

注意:官方文档中建议只获取when-in-use定位授权

定位服务类型

英文名称 中文名称 when-in-use always
Standard location service 标准定位服务 Supported Supported
Significant-change location service 基站定位服务 Not available Supported
Visits service 位于某地服务 Not available Supported
Region monitoring 区域监控 Not available Supported
iBeacon ranging 蓝牙测距 Supported Supported
Heading service 方向服务 Supported Supported
Geocoding services 地理编码位置服务 Supported Supported

定位步骤

1.获取定位授权

When-in-use

  1. 向Info.plist中添加”Privacy - Location When In Use Usage Description”(或者添加NSLocationWhenInUseUsageDescription键)

  2. 创建并配置CLLocationManager对象

  3. 调用CLLocationManager对象的requestWhenInUseAuthorization()方法

注意:必须要在你的应用的Info.plist文件中添加NSLocationWhenInUseUsageDescription. 如果缺少这个键值,授权请求会立即失败

下方代码是首次启动你的app时,请求when-in-use授权的,如果你的应用授权状态已经确定了,只需要开启你需要的定位服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Requesting authorization to use location services
let locationManager = CLLocationManager()
func enableBasicLocationServices() {
locationManager.delegate = self

switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
break

case .restricted, .denied:
// Disable location features
disableMyLocationBasedFeatures()
break

case .authorizedWhenInUse, .authorizedAlways:
// Enable location features
enableMyWhenInUseFeatures()
break
}
}
}

Always

在when-in-use授权的基础上,需要下面额外的步骤:

  1. 在Info.plist中添加”Privacy - Location Always and When In Use Usage Description” (或者添加NSLocationAlwaysAndWhenInUseUsageDescription键)
  2. 调用requestWhenInUseAuthorization()方法先获取基础定位支持
  3. 调用requestAlwaysAuthorization()方法提高授权水平,见下面的函数

注意:NSLocationWhenInUseUsageDescription和NSLocationAlwaysAndWhenInUseUsageDescription都必须添加,而且只有获取到了when-in-use授权,才能升级为always

1
2
3
4
5
6
7
//after get the authorization of when-in-use
func escalateLocationServiceAuthorization() {
// Escalate only when the authorization is set to when-in-use
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManager.requestAlwaysAuthorization()
}
}

2.判断定位服务是否可用

由于硬件,用户未授权,或未打开网络连接等原因,定位服务可能不可用,下面的函数

函数 详情
locationServiceEnable() 判断是否可以获取用户当前的地理位置坐标信息
significantLocationChangeMonitoringAvailable() 判断是否可用基站定位服务(SIM卡)
isMonitoringAvailable(for:) 判断是否可用区域监控
headingAvailable() 判断设备是否能提供指南针相关的信息
isRangingAvailable() 判断是否可获取与附近蓝牙设备的距离
1
2
3
4
5
6
7
8
//check for the availible of standard location service
func checkForLocationServices() {
if CLLocationManager.locationServicesEnabled() {
// Location services are available, so query the user’s location.
} else {
// Update your app’s UI to show that the location is unavailable.
}
}

3.配置定位参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//移动的最小距离,接受新的位置信息,单位为米
var distanceFilter: CLLocationDistance
//定位数据的精度
var desiredAccuracy: CLLocationAccuracy
//可选精度如下:
//let kCLLocationAccuracyBestForNavigation: CLLocationAccuracy
//使用额外传感器数据帮助导航类应用能达到的最高精度
//let kCLLocationAccuracyBest: CLLocationAccuracy
//可达到的最高精度
//let kCLLocationAccuracyNearestTenMeters: CLLocationAccuracy
//精度在十米以内
//let kCLLocationAccuracyHundredMeters: CLLocationAccuracy
//精度在一百米以内
//let kCLLocationAccuracyKilometer: CLLocationAccuracy
//精度至邻近一公里
//let kCLLocationAccuracyThreeKilometers: CLLocationAccuracy
//精度至邻近三公里

4.获取位置信息

1
2
3
locationManager.startUpdatingLocation() //开始获取并不断更新设备位置信息
locationManager.requestLocation() //单次请求获取当前位置信息
locationManager.stopUpdatingLocation() //停止获取位置信息

protocol CLLocationManagerDelegate
用来接收位置信息的协议

持续获取位置信息的两种方法:

  1. 利用protocol,实现其中optional的locationManager()代理函数,只要新的位置信息产生,下面的函数就会被调用,其中locations是CLLocation类型的数组,最后一个元素是最新的位置数据,可以在函数中对数据进行处理
1
2
3
4
5
6
locationManager.delegate = self //设置代理
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// locations.last is the latest location in the array
let lastLocation = locations.last!
// Do something with the location.
}

注意:需要继承CLLocationManagerDelegate

  1. 利用requestLocation()方法,设置定时器,每隔固定时间调用一次,获取最新的位置信息

位置信息CLLocation类

1
2
3
4
5
6
7
8
9
//CLLocation成员变量
var coordinate: CLLocationCoordinate2D //地理坐标信息,经纬度
var altitude: CLLocationDistance //海拔(米)
var floor: CLFloor? //用户位于建筑物的楼层(特定位置才能获取到)
var horizontalAccuracy: CLLocationAccuracy //位置不确定度的圆周半径(米)
var verticalAccuracy: CLLocationAccuracy //海拔不确定度(米)
var timestamp: Date //位置信息获取的时刻
var speed: CLLocationSpeed //设备的瞬时速度(米/秒)
var course: CLLocationDirection //设备运动方向(相对于北,单位是角度)

官方文档中的定位步骤:

When you are ready to use location services, follow these steps:

  1. Check to see if your app is authorized to use location services and request permission if your app’s authorization status is not yet determined, as described in Requesting Permission to Use Location Services.

  2. Check to see if the appropriate location services are available for you to use, as described in Determining the Availability of Location Services.

  3. Create an instance of the CLLocationManager class and store a strong reference to it somewhere in your app.(Keeping a strong reference to the location manager object is required until all tasks involving that object are complete. Because most location manager tasks run asynchronously, storing your location manager in a local variable is insufficient.)

  4. Assign a custom object to the delegate property. This object must conform to the CLLocationManagerDelegate protocol.

  5. Configure the properties related to the service you intend to use. For example, when getting location updates, always configure the distanceFilter and desiredAccuracy properties.

  6. Call the appropriate method to start the delivery of events.

完整程序

用最高精度获取位置信息,UI显示及更新,并计算总里程

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
import UIKit
import CoreLocation

class FirstViewController: UIViewController, CLLocationManagerDelegate {
public let locationManagerVar = CLLocationManager()
public var locationInformation: [CLLocation] = [CLLocation]()
public var timer:Timer?
public var count: Int = 0
public var lastTime: Date?
public var distanceTotal: Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
enableLocationServices()
escalateLocationServiceAuthorization()
checkForLocationServices()
}
@IBOutlet weak var speedLabel: UILabel!
@IBOutlet weak var heightLabel: UILabel!
@IBOutlet weak var latitudeLabel: UILabel!
@IBOutlet weak var longitudeLabel: UILabel!
@IBOutlet weak var distanceLabel: UILabel!
@IBAction func stopLocationUpdate(_ sender: UIButton) {
locationManagerVar.stopUpdatingLocation()
}

@IBAction func startLocationUpdate(_ sender: UIButton) {
startReceivingLocationChanges()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// locations.last is the latest location in the array
let lastLocation = locations.last!
// Do something with the location.
count = count + 1
speedLabel.text = "速度:" + String(lastLocation.speed) + "m/s"
heightLabel.text = "海拔:" + String(format: "%.2f", lastLocation.altitude)
latitudeLabel.text = "经度:" + String(format: "%.2f", lastLocation.coordinate.latitude)
longitudeLabel.text = "纬度:" + String(format: "%.2f", lastLocation.coordinate.longitude)
if lastLocation.speed != -1.0
{
if lastTime != nil{
let thetaTime = lastLocation.timestamp.timeIntervalSince(lastTime!)
distanceTotal = distanceTotal + thetaTime*lastLocation.speed
lastTime = lastLocation.timestamp
}
else{
lastTime = lastLocation.timestamp
}
}
distanceLabel.text = "里程:" + String(distanceTotal) + "m"
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
if let error = error as? CLError, error.code == .denied {
// Location updates are not authorized.
manager.stopUpdatingLocation()
return
}
// Notify the user of any errors.
}
func startReceivingLocationChanges() {
let authorizationStatus = CLLocationManager.authorizationStatus()
if authorizationStatus != .authorizedWhenInUse && authorizationStatus != .authorizedAlways {
// User has not authorized access to location information.
return
}
// Do not start services that aren't available.
if !CLLocationManager.locationServicesEnabled() {
// Location services is not available.
return
}
// Configure and start the service.
locationManagerVar.desiredAccuracy = kCLLocationAccuracyBest
locationManagerVar.distanceFilter = 1.0 // In meters.
locationManagerVar.delegate = self
locationManagerVar.startUpdatingLocation()
}
func checkForLocationServices() {
if CLLocationManager.locationServicesEnabled() {
// Location services are available, so query the user’s location.
} else {
// Update your app’s UI to show that the location is unavailable.
}
}
func enableLocationServices() {
let status = CLLocationManager.authorizationStatus()
if status != .authorizedAlways || status != .authorizedWhenInUse
{
// Request when-in-use authorization initially
locationManagerVar.requestWhenInUseAuthorization()
}
}
func escalateLocationServiceAuthorization() {
// Escalate only when the authorization is set to when-in-use
// 仅在授权被使用时才获取位置升级为一直获取位置
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
locationManagerVar.requestAlwaysAuthorization()
}
}

}
Thank you for every coin~