一 前言
ios8.0之后与ios8.0之前,对于CoreLocation的使用,还是有很大区别的。本文介绍下CLLocationManager类的基本使用、如何做简单的区域监听以及一个指南针小案例。
二 CLLocationManager类的基本使用及ios8.0与ios9.0的对比
首先要导入头文件
"#import <CoreLocation/CoreLocation.h>"
需要获取用户位置信息时,要创建CLLocationManager位置管理者对象。
创建一个位置管理者
@property (nonatomic, strong) CLLocationManager *lm;
现在先懒加载下lm对象
#pragma mark - 懒加载
- (CLLocationManager *)lm{
if (!_lm) {
_lm = [[CLLocationManager alloc] init];
_lm.delegate = self; // 设置代理
// 每隔多少米定位一次,如果不设置该值就会频繁调用代理方法"didUpdateLocations"方法
_lm.distanceFilter = 100;
// desiredAccuracy 属性的取值,查看头文件,搜索CLLocationAccuracy,可以找到如下内容
// extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation // 最适合导航
// extern const CLLocationAccuracy kCLLocationAccuracyBest; //最好的
// extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;//10米
// extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;//100米
// extern const CLLocationAccuracy kCLLocationAccuracyKilometer;//1000米
// extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;//300米
// ios8.0之后的定位适配*********************************
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
// 精确度越高,越耗电,定位时间越长
_lm.desiredAccuracy = kCLLocationAccuracyBest;
// 前台定位授权,默认情况下,不可以在后台获取位置,勾选后台模式location update
[_lm requestWhenInUseAuthorization];
// 前后台定位授权(请求永久授权,在Plist里添加key:NSLocationAlwaysUsageDescription)
// [_lm requestAlwaysAuthorization];
}
// 或者先判断_lm对象是否能够响应requestAlwaysAuthorization方法,能响应就执行,这样就不需要知道该方法是作用于8.0之后的
// if ([_lm respondsToSelector:@selector(requestAlwaysAuthorization)]) {
// [_lm requestAlwaysAuthorization];
// }
}
return _lm;
}
在ios9.0后,又多了判断是否可以在后台更新位置allowsBackgroundLocationUpdates
那么我们的适配又要多一个判断
- (CLLocationManager *)lm{
if (!_lm) {
_lm = [[CLLocationManager alloc] init];
_lm.delegate = self;
_lm.distanceFilter = 100;
// 8.0定位适配*********************************
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
_lm.desiredAccuracy = kCLLocationAccuracyBest;
// 前台定位授权,默认情况下,不可以在后台获取位置,要勾选后台模式location update
[_lm requestWhenInUseAuthorization];
}
// ios9.0之后,期望进入后台也能定位,多了这个allowsBackgroundLocationUpdates属性需配置,进入后台运行时,顶部有蓝条,与8.0版本一样
// 所以遇到了版本是9.0的用户,又要适配下
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
// command点击该属性进入文件查看,会发现警告你使用该属性时,一定要勾选后台模式location updates
_lm.allowsBackgroundLocationUpdates = YES;
}
}
return _lm;
}
当位置改变后,通过代理来告知我们。
// 点击屏幕,开始测试
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.lm startUpdatingLocation];
// [self.lm requestLocation];// 这个方法现在已经不常用了,了解即可,该方法头文件使用说明说,其不能与startUpdatingLocation or allowDeferredLocationUpdates同时用
// 测试下distanceFromLocation方法,纬度上相差1度,地理上大概相差111km
// CLLocation *lo1 = [[CLLocation alloc] initWithLatitude:21.77 longitude:22.99];
// CLLocation *lo2 = [[CLLocation alloc] initWithLatitude:22.77 longitude:22.99];
// CLLocationDistance distance = [lo1 distanceFromLocation:lo2];
// NSLog(@"distance = %f",distance);
// 结果:2017-03-06 17:03:47.123 地图ios8.0+[90641:12476302] distance = 110733.934092 (大概是111km)
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{
NSLog(@"定位到了");
// 拿到数组里最新的数据lastObject
/**
* CLLocation详解
* coordinate :坐标,经纬度
* altitude:海拔
* course:航向
* speed:速度
* latitude:纬度
* longtitude:经度
*/
CLLocation *location = [locations lastObject];
/**
* 场景演练:打印当前用户的行走方向,偏离角度以及对应的行走距离,
* 例如:”北偏东30度方向,移动了8米”
*
*/
// 1 获取方向偏向
NSString *angleStr = nil;
switch ((int)location.course / 90) {
case 0:
angleStr = @"北偏东";
break;
case 1:
angleStr = @"东偏南";
break;
case 2:
angleStr = @"南偏西";
break;
case 3:
angleStr = @"西偏北";
break;
default:angleStr = @"跑沟里去了";
break;
}
// 2 偏向角度
NSInteger angle = 0;
angle = (int)location.course % 90;
// 正方向
if (angle == 0) {
angleStr = [angleStr substringToIndex:1];
angleStr = [NSString stringWithFormat:@"正%@",angleStr];
}
// 3 移动多少米
double distance = 0;
if (_oldL) {
distance = [location distanceFromLocation:_oldL];
}
_oldL = location;
// 4 拼串打印
NSString *noticeStr = [NSString stringWithFormat:@"%@%zd度方向,移动了%f米",angleStr,angle,distance];
NSLog(@"%@",noticeStr);
}
/**
授权状态发生改变时调用
@param manager 位置管理者
@param status 状态
*/
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
// CLAuthorizationStatus是枚举类型,我们可以依照源文件,来解析下当前的用户位置状态
NSLog(@"status = %d",status);
}
/**
定位失败(配合requestLocation方法)
@param manager <#manager description#>
@param error <#error description#>
*/
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
}
打开模拟器,选择你需要的模拟位置变化状态。
三 区域监听
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *lM;
@end
@implementation ViewController
// 懒加载
- (CLLocationManager *)lM{
if (!_lM) {
_lM = [[CLLocationManager alloc] init];
_lM.delegate = self;
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
[_lM requestAlwaysAuthorization];
}
}
return _lM;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 给定区域region,标识为@"qingyun"
CLLocationCoordinate2D center = {21.33,123.99};
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:1000 identifier:@"qingyun"]; //identifier是用来标识具体是哪个区域的
// 开始监听区域region
[self.lM startMonitoringForRegion:region];
// 给定区域regin2,标识为@"qingyun2"
CLLocationCoordinate2D center2 = {33.33,123.99};
CLCircularRegion *regin2 = [[CLCircularRegion alloc] initWithCenter:center2 radius:1000 identifier:@"qingyun2"];
// 开始监听区域region2
[self.lM startMonitoringForRegion:regin2];
// 请求指定区域的状态(状态包括:1 无法监听到 2 在该区域内 3 在该区域外)
[self.lM requestStateForRegion:region];
}
#pragma mark - CLLocationManagerDelegate
//进入区域,只有当你动态进入时,才会调用
- (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{
NSString *str = nil;
// CLRegionStateUnknown,
// CLRegionStateInside,
// CLRegionStateOutside
switch (state) {
case CLRegionStateUnknown:
str = @"CLRegionStateUnknown";
break;
case CLRegionStateInside:
str = @"CLRegionStateInside";
break;
case CLRegionStateOutside:
str = @"CLRegionStateOutside";
break;
default:
str = @"我不知道";
break;
}
NSLog(@"state = %@ region.identifier = %@",str,region.identifier);
}
//运行之后会打印:
//[94660:13100043] state = CLRegionStateInside region.identifier = qingyun
//将模拟器经纬度从:{21.33,123.99}改成:{22.33,123.99}后会有如下打印:
//2017-03-10 11:13:48.246 CoreLocation-区域监听[94660:13100043] 离开区域,qingyun
//2017-03-10 11:13:48.247 CoreLocation-区域监听[94660:13100043] state = CLRegionStateOutside region.identifier = qingyun
@end
模拟器Debug->Location->CustomLocation 可以修改下当前位置
四 指南针的基本实现
首先选择一个指南针图片到你的项目里
单纯的做指南针(指向的方向其实是磁极的南和北)是不需要授权的,因为获取用户方向不牵扯到用户隐私
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *lm;
@property (weak, nonatomic) IBOutlet UIImageView *compass;
@end
@implementation ViewController
#pragma mark - 懒加载
- (CLLocationManager *)lm{
if (!_lm) {
_lm = [[CLLocationManager alloc] init];
_lm.delegate = self;
// 每隔多少度更新一次
_lm.headingFilter = 2;
}
return _lm;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 开始更新设备朝向
[self.lm startUpdatingHeading];
}
#pragma mark - CLLocationManagerDelegate
/**
获取到手机朝向时调用
@param manager 位置管理者
@param newHeading 朝向对象
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
/**
* CLHeading
* magneticHeading:磁北角度
* trueHeading:真北角度
*/
NSLog(@"%f",newHeading.magneticHeading);
// 角度转弧度
CGFloat angle = newHeading.magneticHeading;
CGFloat angleR = angle / 180.0 * M_PI;
[UIView animateWithDuration:0.25 animations:^{
// 让指南针上指向北边的指针,始终指向北边,与人转动方向相反
self.compass.transform = CGAffineTransformMakeRotation(-angleR);
}];
}
@end