
我在哪?我是谁?我要去哪?
定位简述
设备通过AGPS、蓝牙(iBeancon)以及WiFi进行定位。

- GPS 在搜星条件比较好的情况下精度在10米~20米左右
- 基站定位 主要看基站密度,一般在百米~数公里
- AGPS 利用手机基站信号结合GPS卫星信号,加快定位速度
- 蓝牙定位 主要应用于室内定位,利用蓝牙设备持续发送的广播帧实现
- WiFi定位 利用扫描附近wifi时获取到的多个mac地址与服务器中的mac地址库匹配,加上信号强弱计算从而定位,服务器中的mac地址多数为地图街景车扫描到的
初窥

| 类名 | 用途 |
|---|---|
| CLAvailability | 定位可用性(不需开发来引用进行使用) |
| CLErrorDomain、CLError | 报错Domain和type声明 |
| CLRegion CLCircularRegion CLBeaconRegion |
位置范围 |
| CLHeading | 朝向信息 |
| CLLocation | 位置信息 |
| CLLocationManager | 定位管理类 |
| CLLocationManagerDelegate | 定位管理类协议 |
| CLLocationManager+CLVisitExtensions | 定位管理类扩展方法 |
| CLPlacemark | 反地理编码信息 |
| CLGeocoder | (反)地理编码 |
| CLVisit | 访问位置信息 |
基本使用
定位权限
iOS 8之前
- 前台定位不需要通过特定方法请求权限
- 后台定位需要在target配置-Capabilities-Background Modes开启,并勾选Location updates。

iOS 8之后
需要在info.plist中设置:
- 前台定位:
NSLocationWhenInUseUsageDescription - 后台定位: 前台定位基础上 +
NSLocationAlwaysUsageDescription

- 前台定位需要通过
CLLocationManager的对象方法-requestWhenInUseAuthorization请求获取权限 - 后台定位
有一下两种方法,前一种在App进入后台后,状态栏会展示蓝色条(“xxx”正在使用使用您的位置信息),后一种不会。- 在前台定位基础上,在Background Modes中勾选Location updates
- 通过
CLLocationManager的对象方法-requestWhenInUseAuthorization请求获取前台定位权限
iOS 11之后
需要在info.plist中设置:
- 前台定位: 与iOS8一样
- 后台定位: 在iOS8基础上增加
NSLocationAlwaysAndWhenInUseUsageDescription

权限检查
通过
CLLocationManager的类方法检查定位服务开启以及定位授权情况1
2
3
4
5//获取定位服务是否开启
BOOL enable = [CLLocationManager locationServicesEnabled];
//定位授权情况
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];通过
CLLocationManager的代理方法,监听授权变化情况1
2
3- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
NSLog(@"%ld", status);
}
| 权限状态 | 说明 |
|---|---|
| kCLAuthorizationStatusNotDetermined | 用户还未决定是否使用定位 |
| kCLAuthorizationStatusRestricted | 定位服务授权状态是受限制 (可能不是用户拒绝的) |
| kCLAuthorizationStatusDenied | 用户拒绝授权 设置中定位服务关闭 |
| kCLAuthorizationStatusAuthorizedAlways | 允许任何时候定位 |
| kCLAuthorizationStatusAuthorizedWhenInUse | 允许App运行时定位 |
| kCLAuthorizationStatusAuthorized | 已废弃,等同 kCLAuthorizationStatusAuthorizedAlways |
前台定位
使用CLLocationManager的对象方法-startUpdatingLocation获取位置信息,通过其代理方法获取定位信息与错误信息,定位会根据配置的精度和更新距离自行处理更新位置信息的频率,鉴于定位对电量的消耗,注意在适当的时候调用-stopUpdatingLocation进行停止。
进行前台定位
可以通过对象属性desiredAccuracy设置精度,distanceFilter设置触发回调更新的距离1
2
3
4
5
6
7
8
9
10
11
12
13self.manager = [[CLLocationManager alloc] init];
self.manager.delegate = self;
//配置精度
//定位精度,精度高,使用点多,默认为kCLLocationAccuracyBest
self.manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
//地图上每隔多少米才更新一次,低于这个距离的定位不会回调delegate
self.manager.distanceFilter = 200;
//进行定位
//持续请求,需要在合适的时候使用-stopUpdatingLocation停止
[self.manager startUpdatingLocation];回调定位结果
通过locationManager:didUpdateLocations:获取定位结果信息,位置信息是一个数组,其可能没有或包含一个或者多个CLLocation位置信息对象,结合App用途实际情况进行使用。
通过locationManager:didFailWithError:获取定位失败的错误信息,error相关的类型声明在CLError以及CLErrorDomain中可以看到。1
2
3
4
5
6
7
8
9//定位结果回调
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(nonnull NSArray<CLLocation *> *)locations {
NSLog(@"回调位置数组-->%@\n",locations);
}
//定位失败回调
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"定位失败错误信息:%@\n", error);
}
补充
iOS 9新增了定位方法-requestLocation,此方法会以精度从低到高进行请求定位,以获取到精度最高的位置信息,通过代理方法返回位置信息。
如果最终获得到的位置信息精度不够高的话,依然会在超时后通过代理方法返回位置信息。如果未获得到位置信息,会通过失败代理进行回调(使用此方法定位必须实现失败回调)。
此方法只会回调一次定位结果,不需要使用-stopUpdatingLocation进行停止。
注意,如果与-startUpdatingLocation同时使用,本方法的定位请求会被取消。
后台定位
主要是请求用户权限上的区别,定位方法依然是使用-startUpdatingLocation开启定位,然后通过代理方法locationManager:didUpdateLocations:获取定位结果信息
在iOS 9后CLLocationManager新增了一个allowsBackgroundLocationUpdates的属性,默认为NO,如果在未设置target配置-Capabilities-开启Background Modes并勾选Location updates的情况下,需要进行后台定位,必须给这个属性设置为YES;若在工程中配置后台模式,此值页推荐是设置为YES,虽然NO的时候影响也不大。
范围监听
通过设定一个/多个地理封闭区域,结合定位信息来判断设备进入/离开所设定的区域,并在这些时候做出一些响应。
- 使用
CLLocationManager类方法+isMonitoringAvailableForClass:判断是否可以进行监听 - 创建
CLRegion/CLCircularRegion对象设定一个封闭区域 - 使用
-startMonitoringForRegion:方法开始监听位置 - 实现代理方法获取监听信息

启动监听
1 | //判断是否可以监听 |
监听回调
1 | // 进入监听区域回调 |
也可以通过-requestStateForRegion:方法获取当前定位与某个区域的状态,结果会通过代理方法locationManager:didDetermineState:region:进行回调
方位指向
通过CoreLocation也可以获取到设备的方位,使用CLLocationManager的对象方法-startUpdatingHeading可以实时获取设备方位。
其结果通过代理方法locationManager:didUpdateHeading:进行回调,返回一个CLHeading类的对象。
CLHeading对象主要属性
| 属性 | 简述 |
|---|---|
| magneticHeading | 磁北极(0~359.9),0为磁北极 |
| trueHeading | 真北极(0~359.9),0为真北极 |
| headingAccuracy | 方位精度 |
| x | 地磁测量的x |
| y | 地磁测量的y |
| z | 地磁测量的z |
| timestamp | 对象产生的时间戳 |
扩展使用
CoreLocation中也提供了基本的地理编码与反地理编码功能,可以将地址字符串解析为一个CLLocation对象的位置信息,也可将位置信息转义成我们日常使用的文字形式结构化的地址信息以CLPlacemark对象进行呈现。如果需要更多的功能以及地图相关就需要结合MapKit来使用了。
地理编码
地理编码是将统计资料或是地址信息建立空间坐标关系的过程,包含: 正向地理编码、反向地理编码、向量式地理编码、网格式地理编码。在CoreLocation框架中CLGeocoder类提供了正向地理编码和反向地理编码两种常用的地理编码功能
正向地理编码
正向地理编码是将地址/地名描述转化为地球表面上相应位置,如输入“北京”,被转化为“中国 北京市 +39.90498900,+116.40528500”。
使用CLGeocoder类的以下三种对象方法可以进行正向地理编码,结果会通过block进行回调CLPlacemark对象的数组以及错误信息。
1 | //处理地理编码的对象 |
反向地理编码
反向地理编码是将地球表面的地址坐标转换为标准地址的过程,如输入“+39.90498900,+116.40528500”,被转化为“中国 北京市 +39.90498900,+116.40528500”。
使用CLGeocoder类的对象方法reverseGeocodeLocation:completionHandler:可以进行反向地理编码,结果会通过block进行回调CLPlacemark对象的数组以及错误信息。
1 | //通过经纬度构建一个CLLocation对象,也可以是通过之前定位获取到的 |
CLPlacemark
| location | CLLocation对象 标准的位置信息 |
|---|---|
| region | CLRegion对象,范围 |
| timeZone | 时区 |
| addressDictionary | 可用于保存到通讯录 AddressBookUI |
| name | 地名 |
| thoroughfare | 街道名 |
| subThoroughfare | 街道相关信息,例如门牌等 |
| locality | 城市名 |
| subLocality | 城市下属区名 |
| administrativeArea | 州,对应中国的省份名称 |
| subAdministrativeArea | 其他行政区域信息 |
| postalCode | 邮政编码 |
| ISOcountryCode | 国家ISO码,如中国CN |
| country | 国家 |
| inlandWater | 水源、湖泊 |
| ocean | 海洋 |
| areasOfInterest | 关联的或利益相关的地标 |
如:浙江杭州梦想小镇定位反地理编码后结果

补充
- 直辖市在之前通过
subLocality为nil来判断,但最近接口结果变成了,直辖市的subLocality和administrativeArea是相同的,并不为会空。 CLGeocoder会根据设备语言进行请求,使返回的CLPlacemark对象的信息是本地化的语言,而且并没有开放接口用于在请求前设置语言。
如果需要在设置语言为英文的设备上,依然返回中文的CLPlacemark对象信息,可以通过如下方法设置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 保存 Device 的现语言 (英语 法语 等)
NSMutableArray *userDefaultLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
// 强行设置为中文
[[NSUserDefaults standardUserDefaults] setObject:@[@"zh-hans"] forKey:@"AppleLanguages"];
//开始请求反向地理编码
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
NSLog(@"反向地理编码 -> 结果:%@ \n\t错误:%@\n", placemarks, error);
}];
// 还原Device 的语言(延迟0.5秒,如果直接执行有很大概率依然是原设备语言)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSUserDefaults standardUserDefaults] setObject:userDefaultLanguages forKey:@"AppleLanguages"];
});
距离计算
通过CLLocation的对象方法distanceFromLocation:可以计算两个Location的距离
1 | //天津loaction |