CoreLocation
用于地理定位,地理编码,区域监听等(着重功能实现)
1、导入主头文件 #import <CoreLocation/CoreLocation.h>
2、使用CLLocationManager对象来做用户定位
iOS 8.0 以前
1.创建位置管理者
#pragma mark - lazy
- (CLLocationManager *)manager
{
if (_manager == nil) {
//1.创建位置管理者
_manager = [[CLLocationManager alloc] init];
//2.告诉外界位置 (代理、block)
_manager.delegate = self;
//设置每隔多远定位一次
// 设置每隔多远定位一次(1次 111km/100m)
// 最新的位置距离上一次位置之间的距离大于100m, 才会通过代理告诉外界
//_locationM.distanceFilter = 100;
//定位精确度
_manager.desiredAccuracy = kCLLocationAccuracyBest;
}
return _manager ;
}
定位精度
//定位精度越高越耗电,定位时间越长
// kCLLocationAccuracyBestForNavigation // 最适合导航
// kCLLocationAccuracyBest; // 最好的
// kCLLocationAccuracyNearestTenMeters; // 附近10米
// kCLLocationAccuracyHundredMeters; // 100米
// kCLLocationAccuracyKilometer; // 1000米
// kCLLocationAccuracyThreeKilometers; // 3000米
使用位置管理者, 开始获取用户位置
// 一旦调用了这个方法, 那么就会不断的获取用户位置信息, 然后告诉外界
// 默认情况,只能在前台获取用户位置信息, 如果我们想要在后台获取位置, 必须勾选后台模式 location updates
// 标准定位服务(基于gps/wifi/基站)
[self.manager startUpdatingLocation]; //不停监听
代理CLLocationManagerDelegate
#pragma mark - CLLocationManagerDelegate
/**
* 当获取到用户位置信息时调用
* @param manager 位置管理者
* @param locations 位置数组
*/
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(@"定位到了用户的信息");
// 一般我们开发中, 获取到用户位置信息之后, 做一些业务逻辑操作
// 针对于定位一次的情况, 可以在定位到之后 停止获取用户位置
// [manager stopUpdatingLocation];
}
iOS 8.0 以后
8.0的API,主动请求用户授权
// 请求前台定位授权
- (void)requestAlwaysAuthorization //请求允许在前后台都能获取用户位置的授权
// 默认情况下, 只能在前台获取用户位置
// 如果想要获取后台位置, 需要勾选后台模式 location updates , 但是会出现蓝条
// 请求前后台定位授权
- (void)requestWhenInUseAuthorization//请求允许在前台获取用户位置的授权
// 默认在前后台都可以获取用户位置信息, 无论是否勾选后台模式locaiton updates, 而且不会出现蓝条
//**-------ios8.0+定位适配---------- */
1、if([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
2、if([_locationM respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationM requestAlwaysAuthorization];
}
// 如果授权状态发生变化时,调用
// status : 当前的授权状态
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusNotDetermined:
{
NSLog(@"用户未决定");
break;
}
case kCLAuthorizationStatusRestricted:
{
NSLog(@"受限制");
break;
}
case kCLAuthorizationStatusDenied:
{
// 判断当前设备是否支持定位, 并且定位服务是否开启()
if([CLLocationManager locationServicesEnabled])
{
NSLog(@"定位开启,被拒绝");
// ios8,0- 需要截图提醒引导用户
// iOS8.0+
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url])
{
[[UIApplication sharedApplication] openURL:url];
}
}else
{
NSLog(@"定位服务关闭");
}
break;
}
case kCLAuthorizationStatusAuthorizedAlways:
{
NSLog(@"前后台定位授权");
break;
}
case kCLAuthorizationStatusAuthorizedWhenInUse:
{
NSLog(@"前台定位授权");
break;
}
default:
break;
}
}
iOS 9.0以后
// 如果在iOS9.0+想要在前台授权模式下, 在后台获取用户位置, 我们需要额外的设置以下属性为YES
if (isIOS(9.0)) {
_locationM.allowsBackgroundLocationUpdates = YES;
}
//前后台授权模式下,在后台定位还需要在plist文件里额外设置,如下图
// 请求一次位置信息
// 注意 不能与startUpdatingLocation 一起使用
if(isIOS(9.0))
{
[self.locationM requestLocation];
/*
按照定位精确度从低到高进行排序,逐个进行定位。如果获取到的位置不是精确度最高的那个,也会在定位超时后,通过代理告诉外界(必须实现代理的-locationManager:didFailWithError:方法, 不能与startUpdatingLocation方法同时使用)
*/
}
// 当获取到用户位置信息时调用
// manager : 位置管理者
// locations: 位置数组 按时间进行排序, 如果想要拿到最新的位置, 直接拿最后一个
// id+泛型 is kind of
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
// coordinate : 经纬度坐标
// altitude : 海拔
// horizontalAccuracy : 如果是负数, 代表当前位置不可用
// course : 航向(0---359.0)
// distanceFromLocation : 计算两个点之间的物理距离
// 判断当前位置是否可用
CLLocation *location = [locations lastObject];
if(location.horizontalAccuracy < 0)
return;
// 场景演示:打印当前用户的行走方向,偏离角度以及对应的行走距离,
// 例如:” 北偏东 30度 方向,移动了 8 米”
// 1. 确定航向
NSString *angleStr;
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 = (int)location.course % 90;
if(angle == 0)
{
angleStr = [angleStr substringToIndex:1];
}
// 确定行走了多少米
double distance = 0;
if (_lastLocation) {
distance = [location distanceFromLocation:_lastLocation];
}
_lastLocation = location;
// 例如:” 北偏东 30度 方向,移动了 8 米”
NSString *noticeStr;
if (angle == 0) {
noticeStr = [NSString stringWithFormat:@"正%@方向, 移动了%f米", angleStr, distance];
}else
{
noticeStr = [NSString stringWithFormat:@"%@%zd方向, 移动了%f米", angleStr, angle, distance];
}
NSLog(@"%@", noticeStr);
// NSLog(@"定位到了--%@", location);
// 一般我们开发中, 获取到用户位置信息之后, 做一些业务逻辑操作
// 针对于定位一次的情况, 可以在定位到之后 停止获取用户位置
// [manager stopUpdatingLocation];
}
区域监听
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
/** 位置管理者 */
@property (nonatomic, strong) CLLocationManager *locationM;
@property (weak, nonatomic) IBOutlet UILabel *noticeLabel;
@end
@implementation ViewController
#pragma mark -懒加载
-(CLLocationManager *)locationM
{
if (!_locationM) {
_locationM = [[CLLocationManager alloc] init];
_locationM.delegate = self;
// 主动请求定位授权
if([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
[_locationM requestAlwaysAuthorization];
}
return _locationM;
}
- (void)viewDidLoad {
/** 如果想要区域监听, 必须获取用户的定位授权 */
// 监听的区域个数有限制
// 判定某个区域对应的类, 能否被监听
if([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]])
{
// 0. 创建一个区域
// 1.确定区域中心
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.345);
// 确定区域半径
CLLocationDistance radius = 1000;
// 使用前必须判定当前的监听区域半径是否大于最大可被监听的区域半径
if(radius > self.locationM.maximumRegionMonitoringDistance)
{
radius = self.locationM.maximumRegionMonitoringDistance;
}
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"小码哥"];
// 区域监听
// [self.locationM startMonitoringForRegion:region];
// 请求区域状态(如果发生了进入或者离开区域的动作也会调用对应的代理方法) 刚开始启动时候不知道是在进入还是离开
[self.locationM requestStateForRegion:region];
}
}
#pragma mark -CLLocationManagerDelegate
/**
* 进入区域调用(是一个动作)
*
* @param manager 位置管理者
* @param region 区域
*/
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@"进入区域");
self.noticeLabel.text = @"小码哥欢迎你, 给你技术";
}
/**
* 离开某个区域调用
*
* @param manager 位置管理者
* @param region 区域
*/
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(@"离开区域");
self.noticeLabel.text = @"祝大神三期找到30K";
}
/**
* 请求某个区域的状态是调用
*
* @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 = @"祝大神三期找到30K";
}
}
/**
* 监听区域失败时调用
*
* @param manager 位置管理者
* @param region 区域
* @param error 错误
*/
-(void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error
{
// 经验: 一般在这里, 做移除最远的区域
// [manager stopMonitoringForRegion:最远区域]
}
@end
地理编码和反地理编码
使用CLGeocoder可以完成“地理编码”和“反地理编码”
地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
反地理编码:根据给定的经纬度,获得具体的位置信息
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()
/** 地理编码 */
@property (nonatomic, strong) CLGeocoder *geoC;
@property (weak, nonatomic) IBOutlet UITextView *addressTV;
@property (weak, nonatomic) IBOutlet UITextField *latitudeTF;
@property (weak, nonatomic) IBOutlet UITextField *longitudeTF;
@end
@implementation ViewController
#pragma mark -懒加载
-(CLGeocoder *)geoC
{
if (!_geoC) {
_geoC = [[CLGeocoder alloc] init];
}
return _geoC;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//
[self.view endEditing:YES];
}
/**
* 地理编码(地址转经纬度)
*/
- (IBAction)geoCoder {
NSString *address = self.addressTV.text;
// 容错
if([address length] == 0)
return;
[self.geoC geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
// CLPlacemark : 地标
// location : 位置对象
// addressDictionary : 地址字典
// name : 地址详情
// locality : 城市
if(error == nil)
{
CLPlacemark *pl = [placemarks firstObject];
self.addressTV.text = pl.name;
self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue;
self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue;
}else
{
NSLog(@"错误");
}
}];
}
- (IBAction)reverseGeoCoder {
// 获取用户输入的经纬度
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) {
if(error == nil)
{
CLPlacemark *pl = [placemarks firstObject];
self.addressTV.text = pl.name;
self.latitudeTF.text = @(pl.location.coordinate.latitude).stringValue;
self.longitudeTF.text = @(pl.location.coordinate.longitude).stringValue;
}else
{
NSLog(@"错误");
}
}];
}
@end