ios-CoreLocation
ios原生定位CoreLocation
1.使用定位
#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
打印输出
<+37.32462634,-122.02381665> +/- 5.00m (speed 3.51 mps / course 267.11) @ 16/5/10 中国标准时间 上午8:42:58
... ...
[_locationManger requestAlwaysAuthorization]
如果你的info.plist 中没有配置相关的key。这行代码什么都不会做的。
适配iOS8 9
#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 授权
// iOS8.0- , 截图
// iOS8.0+ , 通过调用一个方法, 来直接到达设置界面
// 跳转核心代码
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL: url];
}
2.CLLocation详解
/**
* 当定位到之后, 进入这个方法
*
* @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);
}
3.获取当前设备朝向(指南针)
// 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);
}];
}
4.区域监听
// -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 - CLLocationManagerDelegate
#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 = @"欢迎下次来复读";
}
}
5.(反)地理编码
地理编码管理器初始化
/** 地理编码管理器 */
@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;
}
}];
}
5.第三方框架INTULocationManager
6.代理模式到block模式的转换
//
// 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
掉用
//
// 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