CoreLocation框架

简介

在移动互联网时代,移动 app 能解决用户的和诺生活琐事,比如:

周边:找参观,找 KTV、找电影院等等

导航:根据用户设定的起点和中点,进行路线桂法,并指引用户如何到达

在上述应用中,都用到了定位和地图功能,在 iOS 开发中,要想加入这两大功能,必须基于2个框架进行开发

CoreLocation:用于地理定位,地理编码,区域监听等(着重功能实现)
MapKit:用于地图展示,例如大头针,路线、覆盖层展示等(着重界面展示)

地理编码:将详细地址转换成经纬度
反地理编码:将经纬度转换成详细地址

2个热门专业术语

LBS:Location Based Service
SoLoMo:Social Local Mobile(索罗门)

CoreLocation在 iOS8.0之前的简单实用

使用须知:
CoreLocation 框架中所有数据类型的前缀都是 CL
CoreLocation 中使用 CLLocationManager 对象来做用户定位

注意:
用户隐私保护,需要提高用户点击允许获取位置信息的成功率。需要在 info.plist 加入字段
Privacy - Location Usage Description 值为:会出现在弹框的中间

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
/**
* 位置管理者
*/
- (CLLocationManager *)locationManager
{
if (_locationManager == nil) {

_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;

// 位置过滤 (每隔100米定位一次)
_locationManager.distanceFilter = 100;

// 定位精确度
/*
kCLLocationAccuracyBestForNavigation __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);(最适合导航)
kCLLocationAccuracyBest;(最好的)
kCLLocationAccuracyNearestTenMeters;(附近10米)
kCLLocationAccuracyHundredMeters;(附近100米)
kCLLocationAccuracyKilometer;(附近1000米)
kCLLocationAccuracyThreeKilometers;(附近3000米)
*/

// 精确度越高,越耗电,定位时间越长
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
}
return _locationManager;
}

注意,后台持续运行: (iOS8.0之前的方法)

CLLocationManagerDelegate

更新到位置之后调用

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
/**
* 更新到位置之后调用
*
* @param manager 位置管理者
* @param location 位置数组
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
NSLog(@"定位到了");
// 拿到位置做一些业务逻辑操作

// 停止更新业务逻辑
[manager stopUpdatingLocation];

// 每隔多远进行定位 (每隔10米进行定位)
}

/**
* 授权状态发生改变时会调用
* @param status 判断用户是否授权
*/
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
// 用户还未决定
case kCLAuthorizationStatusNotDetermined:
{
NSLog(@"用户还未决定");
break;
}
// 问受限
case kCLAuthorizationStatusRestricted:
{
NSLog(@"访问受限");
break;
}
// 定位关闭时和对此APP授权为never时调用
case kCLAuthorizationStatusDenied:
{
// 定位是否可用(是否支持定位或者定位是否开启)
if([CLLocationManager locationServicesEnabled])
{
NSLog(@"定位开启,但被拒");
}else
{
NSLog(@"定位关闭,不可用");
}
// NSLog(@"被拒");
break;
}
// 获取前后台定位授权
case kCLAuthorizationStatusAuthorizedAlways:
// case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
{
NSLog(@"获取前后台定位授权");
break;
}
// 获得前台定位授权
case kCLAuthorizationStatusAuthorizedWhenInUse:
{
NSLog(@"获得前台定位授权");
break;
}
default:
break;
}

}

/**
* 定位失败
*/
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"更新失败");
}

获取到手机朝向时调用

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
/**
* locationManager
*/
- (CLLocationManager *)locationManager
{
if (_locationManager == nil) {

_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
// 每隔2度更新一次
_locationManager.headingFilter = 2;
}
return _locationManager;
}

/**
* 获取到手机朝向时调用
* newHeading
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
NSLog(@"%.2f", newHeading.magneticHeading);

// 把角度转换成弧度
CGFloat angle = newHeading.magneticHeading;

CGFloat angleRadian = angle / 180.0 * M_PI;

// 旋转图片
[UIView animateWithDuration:0.25 animations:^{
self.compassImageView.transform = CGAffineTransformMakeRotation(-angleRadian);
}];
}

代理方法中有一个 CLHeading 的类,此类用法如下:

1
2
3
4
5

// magnetic North 磁北角度(手机朝向和磁北极的夹角,开发中常用)
@property(readonly, nonatomic) CLLocationDirection magneticHeading;
// ture North 正北角度(手机朝向和正北极的夹角)
@property(readonly, nonatomic) CLLocationDirection trueHeading;

进入或离开区域时调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 进入区域
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@"进入区域%@", region.identifier);

}

// 离开区域
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(@"离开区域%@", region.identifier);
}

/**
* 会调用代理告诉我们区域状态
*/
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{

}

iOS8.0之后的适配

从 iOS8.0开始,苹果进一步加强了对用户隐私保护,当 APP 想访问用户的隐私信息时,系统不再自动弹出一个对话框让用户授权

解决方案:

一、调用 iOS8.0的 APL 主动请求用户授权

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
/***************************iOS8.0之后的定位适配*******************************/
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
/*

When +authorizationStatus != kCLAuthorizationStatusNotDetermined,
(iegenerally after the first call) this method will do nothing.
If the NSLocationAlwaysUsageDescription key is not specified in your
Info.plist, this method will do nothing, as your app will be assumed not
to support Always authorization.
*/

// 前后台定位授权(如果当前授权状态不等于用户未决定的状态,这个方法不会做事情,已经授权过了就不会做事情)
// 当前授权为前台模式时,此方法也会有效
[_locationManager requestAlwaysAuthorization];

/*
If the NSLocationWhenInUseUsageDescription key is not specified in your
Info.plist, this method will do nothing, as your app will be assumed not
to support WhenInUse authorization.
*/

// 前台定位授权 (默认情况下不可以在后台获取位置,勾选后台模式后,会出现推送蓝条)
[_locationManager requestWhenInUseAuthorization];
}


// 其他适配方案
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
/*
If the NSLocationAlwaysUsageDescription key is not specified in your
Info.plist, this method will do nothing, as your app will be assumed not
to support Always authorization.
*/

// 前后台定位授权(当前授权为前台模式时,此方法也会有效)
[_locationManager requestAlwaysAuthorization];

/*
If the NSLocationWhenInUseUsageDescription key is not specified in your
Info.plist, this method will do nothing, as your app will be assumed not
to support WhenInUse authorization.
*/

// 前台定位授权 (默认情况下不可以在后台获取位置,勾选后台模式后,会出现推送蓝条)
[_locationManager requestWhenInUseAuthorization];
}

iOS9.0之后的适配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// iOS9.0之后多的方法

/*
这个方法是这样运作的,
kCLLocationAccuracyBestForNavigation __OSX_AVAILABLE_STARTING(__MAC_10_7,__IPHONE_4_0);(最适合导航)
kCLLocationAccuracyBest;(最好的)
kCLLocationAccuracyNearestTenMeters;(附近10米)
kCLLocationAccuracyHundredMeters;(附近100米)
kCLLocationAccuracyKilometer;(附近1000米)
kCLLocationAccuracyThreeKilometers;(附近3000米)

它会由下往上进行定位,如果在时间内就定位到最合适的位置,就会将位置返回给我们。
否则如果定位到附近100米时就超时了,就会返回100米精确的数据给我们。

不能与下列方法一起用
startUpdatingLocation or allowDeferredLocationUpdates.

*/
[self.locationManager requestLocation];

CLLocation 详解

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
// 坐标,经纬度
// CLLocationDegrees latitude; // 纬度
// CLLocationDegrees longitude; // 经度
@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;

// 海拔
@property(readonly, nonatomic) CLLocationDistance altitude;

// 水平精确度
@property(readonly, nonatomic) CLLocationAccuracy horizontalAccuracy;
// 垂直精确度
@property(readonly, nonatomic) CLLocationAccuracy verticalAccuracy;

// 航向
@property(readonly, nonatomic) CLLocationDirection course;

// 速度
@property(readonly, nonatomic) CLLocationSpeed speed;

// 当前日期
@property(readonly, nonatomic, copy) NSDate *timestamp;

// 楼层 需要适应条件
@property(readonly, nonatomic, copy, nullable) CLFloor *floor;

//
@property (nonatomic, readonly, copy) NSString *description;

// 同下,过期了
- (CLLocationDistance)getDistanceFrom:(const CLLocation *)location
// 计算两个地点的物理距离(直线距离)
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location

示例一

做一个获取一个人的方向、移动多少米、并且打印

核心代码:

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
CLLocation *location = [locations lastObject];

// 1.获取方向偏向
NSInteger direction = location.course / 90;
NSString *directionStr = nil;
switch (direction) {
case 0:
directionStr = @"北偏东";
break;
case 1:
directionStr = @"东偏南";
break;
case 2:
directionStr = @"南偏西";
break;
case 3:
directionStr = @"西偏北";
break;

default:
break;
}

// 2.偏向角度
NSInteger angle = (NSInteger)location.course % 90;

directionStr = [NSString stringWithFormat:@"%@%zd度", directionStr, angle];
// 如果角度为0,则说明是正方向
if (angle == 0) {
NSRange range = NSMakeRange(0, 1);
NSString *subStr = [directionStr substringWithRange:range];
directionStr = [NSString stringWithFormat:@"正%@", subStr];
}
// 移动多少米
double distance = 0.0;
if (self.oldLocation != nil) {
distance = [location distanceFromLocation:self.oldLocation];
}
self.oldLocation = location;

NSString *locationStr = [NSString stringWithFormat:@"%@方向,移动了%.2f米", directionStr, distance];

NSLog(@"%@", locationStr);

示例二

指南针

1
2
3
4
5
6
7
8
9
10
11
NSLog(@"%.2f", newHeading.magneticHeading);

// 把角度转换成弧度
CGFloat angle = newHeading.magneticHeading;

CGFloat angleRadian = angle / 180.0 * M_PI;

// 旋转图片
[UIView animateWithDuration:0.25 animations:^{
self.compassImageView.transform = CGAffineTransformMakeRotation(-angleRadian);
}];

区域监听 CLCircularRegion

1
2
3
4
5
6
7
// 区域监听
CLLocationCoordinate2D circleCenter = {21.13, 123.456};
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:circleCenter radius:1000 identifier:@"lizhao"];
[self.locationManager startMonitoringForRegion:region];

// 请求区域状态(会调用代理告诉我们区域状态)
[self.locationManager requestStateForRegion:region];

CLCircularRegion

1
2
3
4
// 圆心,半径,标示(可能会有很多个区域)
- (instancetype)initWithCenter:(CLLocationCoordinate2D)center
radius:(CLLocationDistance)radius
identifier:(NSString *)identifier;

地理编码和反地理编码

地理编码需要接触到一个新的类 CLGeocoder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 反地理编码
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

// forward geocode requests
// geocodeAddressDictionary:completionHandler: takes an address dictionary as defined by the AddressBook framework.
// You can obtain an address dictionary from an ABPerson by retrieving the kABPersonAddressProperty property.
// Alternately, one can be constructed using the kABPersonAddress* keys defined in <AddressBook/ABPerson.h>.

- (void)geocodeAddressDictionary:(NSDictionary *)addressDictionary completionHandler:(CLGeocodeCompletionHandler)completionHandler;

//
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
- (void)geocodeAddressString:(NSString *)addressString inRegion:(nullable CLRegion *)region completionHandler:(CLGeocodeCompletionHandler)completionHandler;

- (void)cancelGeocode;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[self.geoC geocodeAddressString:@"小码哥" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
/**
* CLPlacemark
location : 位置对象
addressDictionary : 地址字典
name : 地址全称
*/

if(error == nil)
{
NSLog(@"%@", placemarks);
[placemarks enumerateObjectsUsingBlock:^(CLPlacemark * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@", obj.name);
self.addressTV.text = obj.name;
self.laTF.text = @(obj.location.coordinate.latitude).stringValue;
self.longTF.text = @(obj.location.coordinate.longitude).stringValue;
}];
}else
{
NSLog(@"cuowu--%@", error.localizedDescription);
}

}];

第三方框架

INTULocationManager

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
/** 使用注意:如果是iOS8+,记得设置info.plist文件中 NSLocationWhenInUseUsageDescription 或者 NSLocationAlwaysUsageDescription */

/**
* 设置定位精度和超时时常进行定位,定位完成后通过block回调通知开发者
*/
[[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyRoom timeout:10 block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
if (status == INTULocationStatusSuccess) {
NSLog(@"%@", currentLocation);
}else
{
NSLog(@"定位失败");
}

}];
/**
* 设置定位精度和超时时常进行定位,定位完成后通过block回调通知开发者,
* 注意:delayUntilAuthorized 值作用是:超时时常是从什么时候开始计算(用户授权后开始计算==YES, 调用此方法开始计算==NO)
*/
INTULocationRequestID requestID = [[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyRoom timeout:10 delayUntilAuthorized:YES block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
if (status == INTULocationStatusSuccess) {
NSLog(@"%@", currentLocation);
}else
{
NSLog(@"定位失败");
}
}];

/** 可以根据位置请求ID,强制结束一个位置请求,或者取消位置请求 */
// 强制完成位置请求(会调用回调block)
// [[INTULocationManager sharedInstance] forceCompleteLocationRequest:requestID];
// 取消位置请求(不会调用回调block)
[[INTULocationManager sharedInstance] cancelLocationRequest:requestID];
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2015/09/16/CoreLocation%E6%A1%86%E6%9E%B6/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论