#import "ViewController.h" #import <CoreLocation/CoreLocation.h> @interface ViewController ()<CLLocationManagerDelegate> /** 定位管理者 */ @property (nonatomic, strong) CLLocationManager *locationManger; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 开始定位 [self.locationManger startUpdatingLocation]; /* kCLLocationAccuracyBestForNavigation kCLLocationAccuracyBest; kCLLocationAccuracyNearestTenMeters; kCLLocationAccuracyHundredMeters; kCLLocationAccuracyKilometer; kCLLocationAccuracyThreeKilometers; */ // 单次定位请求 // 按照精确度从低到高, 逐个进行定位 // 若是在有效时间内, 定位到, 精确度最高的位置, 直接经过代理告诉外界 // 若是当前尚未定位到精确度最高的位置, 可是已经超时, 这时, 就会把当前精确度对应的位置信息, 经过代理告诉外界 // 若是定位失败, 就会调用定位失败的代理方法 // 不能与 startUpdatingLocation 一块儿使用 // if(isIOS(9.0)) // { // // [self.locationM requestLocation]; // } } #pragma mark - CLLocationManagerDelegate // 定位成功时调用 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { CLLocation *l = [locations lastObject]; NSLog(@"%@",l.description); } // 定位失败时调用 -(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"定位失败"); } - (CLLocationManager *)locationManger{ if (!_locationManger) { _locationManger = [[CLLocationManager alloc] init]; _locationManger.delegate = self; // 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. [_locationManger requestAlwaysAuthorization]; } return _locationManger; } @end
打印输出ios
<+37.32462634,-122.02381665> +/- 5.00m (speed 3.51 mps / course 267.11) @ 16/5/10 中国标准时间 上午8:42:58 ... ...
[_locationManger requestAlwaysAuthorization]
若是你的info.plist 中没有配置相关的key。这行代码什么都不会作的。git
#define isIOS(version) ([[UIDevice currentDevice].systemVersion floatValue] >= version)
/** 位置管理者 */ - (CLLocationManager *)locationM { if (!_locationM) { _locationM = [[CLLocationManager alloc] init]; _locationM.delegate = self; /*----------ios8.0+定位适配-----------*/ if(isIOS(8.0)) { // 请求前台定位受权 // 默认状况下, 只能在前台获取用户位置信息 // 若是想要在后台获取用户位置, 必须勾选后台模式 location updates, 可是 , 相比于iOS8.0-, 会出现一个蓝条不断发提醒用户 [_locationM requestWhenInUseAuthorization]; /*----------ios9.0+定位适配-----------*/ // 若是在iOS9.0 , 当前处前台定位受权状态下, 那么即便勾选了后台模式 location updates, 也不会 获取用户位置, 除非,设置一下属性为YES; // _locationM.allowsBackgroundLocationUpdates = YES; if (isIOS(9.0)) { _locationM.allowsBackgroundLocationUpdates = YES; // self.test = [_locationM allowsBackgroundLocationUpdates]; } // [_locationM requestAlwaysAuthorization]; // 请求先后台定位受权 // 默认状况下, 不管是在前台仍是后台, 均可以获取用户位置信息, 并且不会出现蓝条 // 跟是否勾选后台模式location updates没有关系 // 若是当前的用户受权状态 != 用户未决定状态, 那么这个方法不会有效 } // 适配方案2 // if ([_locationM respondsToSelector:@selector(requestAlwaysAuthorization)]) // { // [_locationM requestAlwaysAuthorization]; // } } return _locationM; }
定位代理,当前定位受权状态发生改变时调用数组
/** * 当前定位受权状态发生改变时调用 * * @param manager 位置管理者 * @param status 状态 */ - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{ switch (status) { case kCLAuthorizationStatusNotDetermined: { NSLog(@"用户未决定"); break; } case kCLAuthorizationStatusDenied: { // 判断当前设备是否支持定位, 定位服务是否开启 if([CLLocationManager locationServicesEnabled]) { NSLog(@"真正被拒绝"); // 提醒给APP 受权 // iOS8.0- , 截图 // iOS8.0+ , 经过调用一个方法, 来直接到达设置界面 // 跳转核心代码 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL: url]; } } else { NSLog(@"定位服务被关闭"); } break; } // 系统预留字段 case kCLAuthorizationStatusRestricted: { NSLog(@"受限制"); break; } case kCLAuthorizationStatusAuthorizedAlways: { NSLog(@"先后台定位受权"); break; } case kCLAuthorizationStatusAuthorizedWhenInUse: { NSLog(@"前台定位受权"); break; } default: break; } }
小插曲app
// 提醒给APP 受权 // iOS8.0- , 截图 // iOS8.0+ , 经过调用一个方法, 来直接到达设置界面 // 跳转核心代码 NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL: url]; }
/** * 当定位到以后, 进入这个方法 * * @param manager 位置管理者 * @param locations 位置数组 * id + 泛型 : 数组里面的对象 与这个对象的关系 is kind of */ -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { // 按时间排序, 若是想要拿到最新的位置, 直接拿最后一个 CLLocation *location = [locations lastObject]; /** coordinate : 经纬度 CLLocationCoordinate2D altitude : 海拔 horizontalAccuracy : 若是是负值, 表明当前位置数据不可用 verticalAccuracy : 海拔 -- 若是是负值, 表明当前海拔数据不可用 course : 航向 (0.0----359.9) speed : 速度 floor : 楼层 方法: distanceFromLocation : 计算两个坐标之间的物理直线距离 */ // NSLog(@"%@", location); // >场景演示:打印当前用户的行走方向,偏离角度以及对应的行走距离, // 例如:”北偏东 30度 方向,移动了 8米” if (location.horizontalAccuracy < 0) { return; } // 1. 肯定当前航向(北偏东) NSInteger index = (int)location.course / 90; NSArray *courseStrArray = @[@"北偏东", @"东偏南", @"南偏西", @"西偏北"]; NSString *courseStr = courseStrArray[index]; // 2. 肯定偏离角度 NSInteger angle = (int)location.course % 90; // 表明是正方向 if (angle == 0) { courseStr = [@"正" stringByAppendingString:[courseStr substringToIndex:1]]; } // 3. 肯定行走距离 CGFloat distance = 0; if (_lastLoc) { distance = [location distanceFromLocation:_lastLoc]; } _lastLoc = location; // 4. 拼串打印 // 例如:”北偏东 30度 方向,移动了 8米” NSString *noticeStr; if(angle != 0) { noticeStr = [NSString stringWithFormat:@"%@ %zd 度方向, 移动了 %f米", courseStr, angle, distance]; }else { noticeStr = [NSString stringWithFormat:@"%@ 方向, 移动了 %f米", courseStr, distance]; } NSLog(@"%@", noticeStr); }
// 1. 获取当前设备朝向("磁力计传感器") [self.locationM startUpdatingHeading];
当前获取到设备朝向(CLLocationManagerDelegate)框架
/** * 当前获取到设备朝向时, 调用 * * @param manager 位置管理者 * @param newHeading 当前设备朝向对象 */ -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { /** * CLHeading * magneticHeading : 距离磁北方向的角度 * trueHeading : 距离真北方向的角度 * headingAccuracy : 若是这个值是负数, 那么表明角度不可用 */ if(newHeading.headingAccuracy < 0) return; // 1. 获取设备朝向(角度) CLLocationDirection angle = newHeading.magneticHeading; // 1.1 把角度 转换成弧度 float radius = angle / 180.0 * M_PI; // 2. 反方向旋转指南针图片(弧度) [UIView animateWithDuration:0.5 animations:^{ self.compassView.transform = CGAffineTransformMakeRotation(-radius); }]; }
// -1. 判断当前设备是否支持区域监听(区域类型) if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) { return; }
// 0. 建立区域中心 CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.234); // 0.1 建立区域半径 CLLocationDistance distance = 1000.0; if(distance > self.locationM.maximumRegionMonitoringDistance) { distance = self.locationM.maximumRegionMonitoringDistance; } // 1. 建立一个区域 CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:distance identifier:@"小码哥"]; // 2. 监听一个区域(只有用户有进入区域, 或者离开区域动做的时候 才会经过代理告诉外界) [self.locationM startMonitoringForRegion:region]; // 请求某个区域的状态 // 不止能够获取到指定区域的状态, 并且当状态发生变化时, 也会调用对应的代理方法, 告诉咱们 [self.locationM requestStateForRegion:region];
代理回调 #pragma mark - CLLocationManagerDelegateide
#pragma mark - CLLocationManagerDelegate /** * 进入指定区域时调用 * * @param manager 位置管理者 * @param region 区域 */ -(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { NSLog(@"进入区域---%@", region.identifier); self.noticeLabel.text = @"小码哥欢迎你, 给你技术"; } /** * 离开指定区域时调用 * * @param manager 位置管理者 * @param region 区域 */ -(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { NSLog(@"离开区域---%@", region.identifier); self.noticeLabel.text = @"欢迎下次来复读"; } /** * 当前请求指定区域状态时, 回调的代理方法 * * @param manager 位置管理者 * @param state 状态 * @param region 区域 */ -(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region { /** * CLRegionStateUnknown, // 不知道 CLRegionStateInside, // 在区域内部 CLRegionStateOutside // 在区域外部 */ if(state == CLRegionStateInside) { self.noticeLabel.text = @"小码哥欢迎你, 给你技术"; }else if (state == CLRegionStateOutside) { self.noticeLabel.text = @"欢迎下次来复读"; } }
/** 地理编码管理器 */ @property (nonatomic, strong) CLGeocoder *geoC; #pragma mark - 懒加载 /** 地理编码管理器 */ - (CLGeocoder *)geoC { if (!_geoC) { _geoC = [[CLGeocoder alloc] init]; } return _geoC; }
// 地理编码(地址关键字 ->经纬度 ) - (IBAction)geoCode { NSString *address = self.addressTV.text; // 容错处理 if([address length] == 0) { return; } // 根据地址关键字, 进行地理编码 [self.geoC geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { /** * CLPlacemark : 地标对象 * location : 对应的位置对象 * name : 地址全称 * locality : 城市 * 按相关性进行排序 */ CLPlacemark *pl = [placemarks firstObject]; if(error == nil) { NSLog(@"%f----%f", pl.location.coordinate.latitude, pl.location.coordinate.longitude); NSLog(@"%@", pl.name); self.addressTV.text = pl.name; self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue; self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue; } }]; }
// 反地理编码(把经纬度---> 详细地址) - (IBAction)reverseGeoCode { double latitude = [self.latitudeTF.text doubleValue]; double longitude = [self.longitudeTF.text doubleValue]; CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark *pl = [placemarks firstObject]; if(error == nil) { NSLog(@"%f----%f", pl.location.coordinate.latitude, pl.location.coordinate.longitude); NSLog(@"%@", pl.name); self.addressTV.text = pl.name; self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue; self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue; } }]; }
// // XMGLocationTool.h // 09-代理模式到block模式的转换 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import <Foundation/Foundation.h> #import <CoreLocation/CoreLocation.h> #import "Singleton.h" typedef void(^ResultBlock)(CLLocation *location, CLPlacemark *placeMark, NSString *errorMsg); @interface XMGLocationTool : NSObject single_interface(XMGLocationTool) /** * 直接获取当前位置信息, 而后经过代码块告诉外界 * * @param block 代码块 */ - (void)getCurrentLocation:(ResultBlock)block; @end
// // XMGLocationTool.m // 09-代理模式到block模式的转换 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import "XMGLocationTool.h" #import <UIKit/UIKit.h> #define isIOS(version) ([[UIDevice currentDevice].systemVersion floatValue] >= version) @interface XMGLocationTool()<CLLocationManagerDelegate> /** 记录要执行的代码块 */ @property (nonatomic, copy) ResultBlock block; /** 位置管理者 */ @property (nonatomic, strong) CLLocationManager *locationM; /** 地理编码管理器 */ @property (nonatomic, strong) CLGeocoder *geoC; @end @implementation XMGLocationTool #pragma mark - 懒加载 /** 位置管理者 */ - (CLLocationManager *)locationM { if (!_locationM) { _locationM = [[CLLocationManager alloc] init]; _locationM.delegate = self; // ios8.0+须要请求受权 if (isIOS(8.0)) { // 要在此处, 请求受权, 可是请求哪一个权限, 无法肯定, 靠其余开发者肯定; // 1. 获取info.plist 文件内容 NSDictionary *infoDic = [[NSBundle mainBundle] infoDictionary]; // NSLog(@"%@", infoDic); // 2. 获取其余开发人员, 填写的key NSString *always = infoDic[@"NSLocationAlwaysUsageDescription"]; NSString *whenInUse = infoDic[@"NSLocationWhenInUseUsageDescription"]; if ([always length] > 0) { [_locationM requestAlwaysAuthorization]; } else if ([whenInUse length] > 0) { [_locationM requestWhenInUseAuthorization]; // 在前台定位受权状态下, 必须勾选后台模式location udpates才能获取用户位置信息 NSArray *services = infoDic[@"UIBackgroundModes"]; if (![services containsObject:@"location"]) { NSLog(@"友情提示: 当前状态是前台定位受权状态, 若是想要在后台获取用户位置信息, 必须勾选后台模式 location updates"); } else { if (isIOS(9.0)) { _locationM.allowsBackgroundLocationUpdates = YES; } } }else { NSLog(@"错误---若是在iOS8.0以后定位, 必须在info.plist, 配置NSLocationWhenInUseUsageDescription 或者 NSLocationAlwaysUsageDescription"); } } } return _locationM; } /** 地里编码管理器 */ - (CLGeocoder *)geoC { if (!_geoC) { _geoC = [[CLGeocoder alloc] init]; } return _geoC; } single_implementation(XMGLocationTool) - (void)getCurrentLocation:(ResultBlock)block { // 先记录代码块, 而后在合适的位置调用这个代码块 self.block = block; // 在此处, 并不能直接获取当前的用户位置, 还有地标 if ([CLLocationManager locationServicesEnabled]) { [self.locationM startUpdatingLocation]; } else { self.block(nil, nil, @"定位服务没有开启"); } } #pragma mark - CLLocationManagerDelegate -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *location = [locations lastObject]; if (location.horizontalAccuracy < 0) { return; } // 在这里, 能够获取到位置信息 // 在这里, 还没发, 获取到地标对象, 因此, 在此处, 要进一步进行反地理编码 [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { if (error == nil) { // 获取地标对象 CLPlacemark *pl = [placemarks firstObject]; // 在此处, 最适合, 执行存储的代码块 self.block(location, pl, nil); } else { self.block(location, nil, error.localizedDescription); } }]; // 关闭定位服务 [manager stopUpdatingLocation]; } @end
掉用this
// // ViewController.h // 09-代理模式到block模式的转换 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
// // ViewController.m // 09-代理模式到block模式的转换 // // Created by xiaomage on 15/10/15. // Copyright (c) 2015年 xiaomage. All rights reserved. // #import "ViewController.h" #import "XMGLocationTool.h" @interface ViewController () @end @implementation ViewController - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [[XMGLocationTool sharedXMGLocationTool] getCurrentLocation:^(CLLocation *location, CLPlacemark *placeMark, NSString *errorMsg) { if([errorMsg length] > 0) { NSLog(@"%@", errorMsg); } else { NSLog(@"%@---%@", placeMark.name, location); } }]; } @end