一 前言
ios8.0之后與ios8.0之前,對(duì)于CoreLocation的使用痕钢,還是有很大區(qū)別的港华。本文介紹下CLLocationManager類的基本使用刻蟹、如何做簡(jiǎn)單的區(qū)域監(jiān)聽以及一個(gè)指南針小案例。
二 CLLocationManager類的基本使用及ios8.0與ios9.0的對(duì)比
首先要導(dǎo)入頭文件
"#import <CoreLocation/CoreLocation.h>"
需要獲取用戶位置信息時(shí)淘捡,要?jiǎng)?chuàng)建CLLocationManager位置管理者對(duì)象藕各。
創(chuàng)建一個(gè)位置管理者
@property (nonatomic, strong) CLLocationManager *lm;
現(xiàn)在先懶加載下lm對(duì)象
#pragma mark - 懶加載
- (CLLocationManager *)lm{
if (!_lm) {
_lm = [[CLLocationManager alloc] init];
_lm.delegate = self; // 設(shè)置代理
// 每隔多少米定位一次,如果不設(shè)置該值就會(huì)頻繁調(diào)用代理方法"didUpdateLocations"方法
_lm.distanceFilter = 100;
// desiredAccuracy 屬性的取值,查看頭文件焦除,搜索CLLocationAccuracy座韵,可以找到如下內(nèi)容
// extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation // 最適合導(dǎo)航
// 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) {
// 精確度越高,越耗電踢京,定位時(shí)間越長(zhǎng)
_lm.desiredAccuracy = kCLLocationAccuracyBest;
// 前臺(tái)定位授權(quán)誉碴,默認(rèn)情況下,不可以在后臺(tái)獲取位置瓣距,勾選后臺(tái)模式location update
[_lm requestWhenInUseAuthorization];
// 前后臺(tái)定位授權(quán)(請(qǐng)求永久授權(quán)黔帕,在Plist里添加key:NSLocationAlwaysUsageDescription)
// [_lm requestAlwaysAuthorization];
}
// 或者先判斷_lm對(duì)象是否能夠響應(yīng)requestAlwaysAuthorization方法,能響應(yīng)就執(zhí)行,這樣就不需要知道該方法是作用于8.0之后的
// if ([_lm respondsToSelector:@selector(requestAlwaysAuthorization)]) {
// [_lm requestAlwaysAuthorization];
// }
}
return _lm;
}
在ios9.0后蹈丸,又多了判斷是否可以在后臺(tái)更新位置allowsBackgroundLocationUpdates
那么我們的適配又要多一個(gè)判斷
- (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;
// 前臺(tái)定位授權(quán)成黄,默認(rèn)情況下呐芥,不可以在后臺(tái)獲取位置,要勾選后臺(tái)模式location update
[_lm requestWhenInUseAuthorization];
}
// ios9.0之后奋岁,期望進(jìn)入后臺(tái)也能定位思瘟,多了這個(gè)allowsBackgroundLocationUpdates屬性需配置,進(jìn)入后臺(tái)運(yùn)行時(shí)闻伶,頂部有藍(lán)條滨攻,與8.0版本一樣
// 所以遇到了版本是9.0的用戶,又要適配下
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
// command點(diǎn)擊該屬性進(jìn)入文件查看蓝翰,會(huì)發(fā)現(xiàn)警告你使用該屬性時(shí)光绕,一定要勾選后臺(tái)模式location updates
_lm.allowsBackgroundLocationUpdates = YES;
}
}
return _lm;
}
當(dāng)位置改變后,通過代理來告知我們畜份。
// 點(diǎn)擊屏幕诞帐,開始測(cè)試
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.lm startUpdatingLocation];
// [self.lm requestLocation];// 這個(gè)方法現(xiàn)在已經(jīng)不常用了,了解即可爆雹,該方法頭文件使用說明說停蕉,其不能與startUpdatingLocation or allowDeferredLocationUpdates同時(shí)用
// 測(cè)試下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);
// 結(jié)果: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(@"定位到了");
// 拿到數(shù)組里最新的數(shù)據(jù)lastObject
/**
* CLLocation詳解
* coordinate :坐標(biāo)慧起,經(jīng)緯度
* altitude:海拔
* course:航向
* speed:速度
* latitude:緯度
* longtitude:經(jīng)度
*/
CLLocation *location = [locations lastObject];
/**
* 場(chǎng)景演練:打印當(dāng)前用戶的行走方向,偏離角度以及對(duì)應(yīng)的行走距離,
* 例如:”北偏東30度方向,移動(dòng)了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 移動(dòng)多少米
double distance = 0;
if (_oldL) {
distance = [location distanceFromLocation:_oldL];
}
_oldL = location;
// 4 拼串打印
NSString *noticeStr = [NSString stringWithFormat:@"%@%zd度方向,移動(dòng)了%f米",angleStr,angle,distance];
NSLog(@"%@",noticeStr);
}
/**
授權(quán)狀態(tài)發(fā)生改變時(shí)調(diào)用
@param manager 位置管理者
@param status 狀態(tài)
*/
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
// CLAuthorizationStatus是枚舉類型驯绎,我們可以依照源文件完慧,來解析下當(dāng)前的用戶位置狀態(tài)
NSLog(@"status = %d",status);
}
/**
定位失敗(配合requestLocation方法)
@param manager <#manager description#>
@param error <#error description#>
*/
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
}
打開模擬器,選擇你需要的模擬位置變化狀態(tài)剩失。
三 區(qū)域監(jiān)聽
#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{
// 給定區(qū)域region屈尼,標(biāo)識(shí)為@"qingyun"
CLLocationCoordinate2D center = {21.33,123.99};
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:1000 identifier:@"qingyun"]; //identifier是用來標(biāo)識(shí)具體是哪個(gè)區(qū)域的
// 開始監(jiān)聽區(qū)域region
[self.lM startMonitoringForRegion:region];
// 給定區(qū)域regin2,標(biāo)識(shí)為@"qingyun2"
CLLocationCoordinate2D center2 = {33.33,123.99};
CLCircularRegion *regin2 = [[CLCircularRegion alloc] initWithCenter:center2 radius:1000 identifier:@"qingyun2"];
// 開始監(jiān)聽區(qū)域region2
[self.lM startMonitoringForRegion:regin2];
// 請(qǐng)求指定區(qū)域的狀態(tài)(狀態(tài)包括:1 無法監(jiān)聽到 2 在該區(qū)域內(nèi) 3 在該區(qū)域外)
[self.lM requestStateForRegion:region];
}
#pragma mark - CLLocationManagerDelegate
//進(jìn)入?yún)^(qū)域拴孤,只有當(dāng)你動(dòng)態(tài)進(jìn)入時(shí)脾歧,才會(huì)調(diào)用
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
NSLog(@"進(jìn)入?yún)^(qū)域,%@",region.identifier);
}
//離開區(qū)域,當(dāng)你動(dòng)態(tài)離開該區(qū)域時(shí)演熟,才會(huì)調(diào)用
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region{
NSLog(@"離開區(qū)域,%@",region.identifier);
}
// 獲取某個(gè)指定區(qū)域的狀態(tài)鞭执,是在該區(qū)域里面,還是在該區(qū)域外面芒粹,還是無法監(jiān)聽到
- (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);
}
//運(yùn)行之后會(huì)打有址摹:
//[94660:13100043] state = CLRegionStateInside region.identifier = qingyun
//將模擬器經(jīng)緯度從:{21.33,123.99}改成:{22.33,123.99}后會(huì)有如下打印:
//2017-03-10 11:13:48.246 CoreLocation-區(qū)域監(jiān)聽[94660:13100043] 離開區(qū)域,qingyun
//2017-03-10 11:13:48.247 CoreLocation-區(qū)域監(jiān)聽[94660:13100043] state = CLRegionStateOutside region.identifier = qingyun
@end
模擬器Debug->Location->CustomLocation 可以修改下當(dāng)前位置
四 指南針的基本實(shí)現(xiàn)
首先選擇一個(gè)指南針圖片到你的項(xiàng)目里
單純的做指南針(指向的方向其實(shí)是磁極的南和北)是不需要授權(quán)的化漆,因?yàn)楂@取用戶方向不牽扯到用戶隱私
#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];
// 開始更新設(shè)備朝向
[self.lm startUpdatingHeading];
}
#pragma mark - CLLocationManagerDelegate
/**
獲取到手機(jī)朝向時(shí)調(diào)用
@param manager 位置管理者
@param newHeading 朝向?qū)ο? */
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading{
/**
* CLHeading
* magneticHeading:磁北角度
* trueHeading:真北角度
*/
NSLog(@"%f",newHeading.magneticHeading);
// 角度轉(zhuǎn)弧度
CGFloat angle = newHeading.magneticHeading;
CGFloat angleR = angle / 180.0 * M_PI;
[UIView animateWithDuration:0.25 animations:^{
// 讓指南針上指向北邊的指針估脆,始終指向北邊,與人轉(zhuǎn)動(dòng)方向相反
self.compass.transform = CGAffineTransformMakeRotation(-angleR);
}];
}
@end