iOS-世界那么大,CoreLocation带你去看看

一. 简介

在我们日常生活中时常用到地图和定位功能,来导航去你想去的地方或者寻找周边的景点,餐厅,电影院等等,在iOS开发中,要想加入这两大功能,必须基于两个框架进行开发,有了这两个框架,想去哪就去哪。 CoreLocation :用于地理定位,地理编码,区域监听等(着重功能实现) MapKit :用于地图展示,例如大头针,路线、覆盖层展示等(着重界面展示)

二. CoreLocation框架的基本使用

1. CoreLocation使用步骤

  1. 导入CoreLocation框架。
  2. 创建CLLocationManager管理者对象。
  3. 遵循代理,并实现代理方法。
  4. 设置获取用户前后台定位授权
  5. 开始定位。

三. CLLocationManager的使用

学习CLLocationManager可以分为三个部分。1.定位 2.手机朝向 3.区域监听

1. CLLocationManager -- 定位

先通过一个简单例子看一下
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property(nonatomic,strong)CLLocationManager *locationM;

@end

@implementation ViewController
-(CLLocationManager *)locationM
{
    if (_locationM == nil) {
        _locationM = [[CLLocationManager alloc]init];
        _locationM.delegate = self;
        // 只在前台开始定位  修改plist文件 提醒用户
        [_locationM requestWhenInUseAuthorization];
        // 前后台都可以定位  修改plist文件 提醒用户
        // [_locationM requestAlwaysAuthorization]; 
    }
    return _locationM;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 开始标准定位服务
    [self.locationM startUpdatingLocation];
    // 开启显著位置变化定位服务
    // [self.locationM startMonitoringSignificantLocationChanges];
}
#pragma mark CLLocationManagerDelegate代理方法
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    // manager 位置管理者       locations 位置数组
    // 在这里拿到位置信息做一些处理,这个方法会被持续调
    NSLog(@"--开始定位--");
}

注意: 1. 获取前台定位授权[_locationM requestWhenInUseAuthorization];需要在plist文件中加入NSLocationWhenInUseUsageDescription 获取前后台定位授权[_locationM requestAlwaysAuthorization]; 需要在plist文件中加入NSLocationAlwaysUsageDescription plist中加入的信息表示获取定位授权时显示的信息。

plist文件修改

运行程序请求用户授权时会弹出

请求用户授权

2. 开启标准定位服务使用的是GPS/WIFI定位,精确度较高,关闭应用程序就无法获取位置,而开启显著位置变化定位服务使用基站定位(必须有电话模块),当应用程序被关闭时,也可以接受到位置通知,并让app进入后台处理,但是定位精确度没有标准定位服务高,耗电少,定位更新频率依照基站密度而定,只要在基站范围内就显示基站位置,当进入另一个基站范围后更新。 如果要求定位及时,精确度高,并且运行时间短,可以使用标准定位服务。 如果长时间监控用户位置,用户移动速度较快,可使用显著位置变化定位服务 3. 代理方法didUpdateLocations会被持续调用,参数manager位置管理者 locations表示位置数组,里面按照时间先后顺序存储CLLocation对象,获取最后一个位置信息[locations lastObject]即可

CLLocationManager -- 关于定位属性和方法
// 判断定位功能是否可用
+ (BOOL)locationServicesEnabled
// 设置过滤单位(米)即每隔多少米定位一次
@property(assign, nonatomic) CLLocationDistance distanceFilter;  
// 设置定位精确度
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
CLLocationAccuracy kCLLocationAccuracyBestForNavigation  // 最适合导航的精确度
CLLocationAccuracy kCLLocationAccuracyBest; // 最好的
CLLocationAccuracy kCLLocationAccuracyNearestTenMeters; // 附近10米范围内
CLLocationAccuracy kCLLocationAccuracyHundredMeters; // 附近100米范围内
CLLocationAccuracy kCLLocationAccuracyKilometer; // 附近1000米范围内
CLLocationAccuracy kCLLocationAccuracyThreeKilometers; // 附近1000米范围内
// 开启定位
- (void)startUpdatingLocation
// 结束定位
- (void)stopUpdatingLocation;
CLLocationManagerDelegate -- 定位常用代理方法
// 定位成功 持续调用
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    // manager : 位置管理者       
    // locations : 位置数组
    // 在这里拿到位置信息做一些处理,这个方法会被持续调
}
// 定位失败时调用
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    // manager : 位置管理者       
    // error : 错误信息
}
// 当用户定位授权状态发生变化时调用
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    // manager : 管理者
    // status :状态
    /*
    kCLAuthorizationStatusNotDetermined = 0 // 用户未决定时
    kCLAuthorizationStatusRestricted // 受限制保留字段
    kCLAuthorizationStatusDenied // 被拒绝 1.被拒绝 2.未开启定位服务
    kCLAuthorizationStatusAuthorizedAlways // 前后台都可以定位授权
    kCLAuthorizationStatusAuthorizedWhenInUse // 前台定位授权
    */
}
CLLocation对象 -- 定位基本属性
// 根据经度和维度创建一个CLLocation对象
- (instancetype)initWithLatitude:(CLLocationDegrees)latitude
    longitude:(CLLocationDegrees)longitude;
// 经纬度,latitude经度 longitude维度
@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;
// 高度位置 可以正面(海拔)或负面(低于海平面)。
@property(readonly, nonatomic) CLLocationDistance altitude;
// 水平精确度,如果是负值表示不可用
@property(readonly, nonatomic) CLLocationAccuracy horizontalAccuracy;
// 垂直精确度,如果是负值表示海拔不可用
@property(readonly, nonatomic) CLLocationAccuracy verticalAccuracy;
// 真北的位置度 取值 0.0 - 359.9 度  0 表示真北 
@property(readonly, nonatomic) CLLocationDirection course 
// 速度 m/s 负值表示速度无效
@property(readonly, nonatomic) CLLocationSpeed speed 
// 定位时间
@property(readonly, nonatomic, copy) NSDate *timestamp;
// 楼层,如果建筑物注册,可以获取楼层
@property(readonly, nonatomic, copy, nullable) CLFloor *floor 
// 返回位置
@property (nonatomic, readonly, copy) NSString *description;
// 计算两个坐标的物理直线距离
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location 

2. CLLocationManager -- 设备方向

手机通过磁力计来判断设备方向,先看一个简单指南针的例子
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property(nonatomic,strong)CLLocationManager *locationM;

@end

@implementation ViewController
-(CLLocationManager *)locationM
{
    if (_locationM == nil) {
        _locationM = [[CLLocationManager alloc]init];
        _locationM.delegate = self;
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.locationM startUpdatingHeading];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    /**
     *  CLHeading
     *  magneticHeading : 距离磁北方向的角度
     *  trueHeading : 距离真北方向的角度
     * headingAccuracy : 如果这个值是负数, 那么代表角度不可用
     */
    if (newHeading.headingAccuracy < 0) {
        return;
    }
    CGFloat angle = newHeading.magneticHeading;
    CGFloat r = angle * M_PI / 180;
    [UIView animateWithDuration:0.5 animations:^{
        self.imageView.transform = CGAffineTransformMakeRotation(-r);
    }];
}
CLLocationManager -- 关于手机朝向属性和方法
// 判断是否支持磁力计定位手机朝向
+ (BOOL)headingAvailable
// 开启手机朝向定位
- (void)startUpdatingHeading 
// 关闭手机朝向定位
- (void)stopUpdatingHeading
CLLocationManagerDelegaer -- 关于手机朝向的代理方法
// 当获取一个新朝向的时候调用,持续调用
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
    // manager : 管理者
    // newHeading : 朝向
}
CLHeading对象基本属性
// 与磁北方向的角度  范围 0.0 - 359.9 度, 0 度表示磁北方向
@property(readonly, nonatomic) CLLocationDirection magneticHeading;
// 与真北方向的角度  范围 0.0 - 359.9 度, 0 度表示真北方向
@property(readonly, nonatomic) CLLocationDirection trueHeading;
// 返回方向值的错误范围,负值表示无效的朝向
@property(readonly, nonatomic) CLLocationDirection headingAccuracy;
// 返回方向的时间
@property(readonly, nonatomic, copy) NSDate *timestamp;

注意:当获取朝向的时候不需要向用户请求授权,因为设备方向不涉及到用户隐私

3. CLLocationManager -- 区域监听

区域监听实例

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property(nonatomic,strong)CLLocationManager *locationM;

@end

@implementation ViewController
-(CLLocationManager *)locationM
{
    if (_locationM == nil) {
        _locationM = [[CLLocationManager alloc]init];
        _locationM.delegate = self;
        [_locationM requestAlwaysAuthorization];
    }
    return _locationM;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 中心
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake(67.666, 80.888);
    // 区域半径
    CLLocationDistance distance = 1000.0;
    CLCircularRegion *range = [[CLCircularRegion alloc]initWithCenter:center radius:distance identifier:@"ding"];
    // 用这个方法需要有位置的变化才行,从外部进来 或者出去才会有响应
    // [self.locationM startMonitoringForRegion:range];
    // 用这个方法就会先获取一次,判断是否在区域中
    [self.locationM requestStateForRegion:range];
}

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    NSLog(@"进入区域");
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    NSLog(@"出去区域");
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    NSLog(@"%@",region);
    // 获取请求指定区域状态时调用的方法
    /**
     CLRegionStateUnknown, // 不知道
     CLRegionStateInside, // 在区域内部
     CLRegionStateOutside // 在区域外部
     */
    if(state == CLRegionStateInside)
    {
        NSLog(@"在区域中");
    }else if (state == CLRegionStateOutside)
    {
        NSLog(@"在区域外面");
    }
}
@end

注意: [self.locationM startMonitoringForRegion:range];开启区域监听,需要有位置的变化才会调用代理方法,例如位置从区域外部进入区域内部。 [self.locationM requestStateForRegion:range];程序一运行就会先确定在不在区域中,当位置发生改变时也会判断在不在区域中,是进入区域还是离开区域

CLLocationManager -- 关于区域间厅属性和方法
// 判断当前设备是否支持区域监听(区域类型)
+ (BOOL)isMonitoringAvailableForClass:(Class)regionClass
// 最大的区域大小,超过这个最大值后无效
@property (readonly, nonatomic) CLLocationDistance maximumRegionMonitoringDistance
// 开启一个区域的监听
- (void)startMonitoringForRegion:(CLRegion *)region 
// 请求一个区域的监听
- (void)requestStateForRegion:(CLRegion *)region 
CLLocationManagerDelegaer -- 关于区域监听的代理方法
// 进入区域时调用 manager : 管理者 region:区域
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
}
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
}
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
       state : 状态
       CLRegionStateUnknown, // 不知道
       CLRegionStateInside, // 在区域内部
       CLRegionStateOutside // 在区域外部
}
CLCircularRegion对象基本属性

CLCircularRegion是CLRegion的子类

// 创建方法 cente : 中心位置 radius : 区域半径 identidier : 唯一标示 
- (instancetype)initWithCenter:(CLLocationCoordinate2D)center
                            radius:(CLLocationDistance)radius
                        identifier:(NSString *)identifier;
// 中心位置
@property (readonly, nonatomic) CLLocationCoordinate2D center;
// 半径
@property (readonly, nonatomic) CLLocationDistance radius;

4. 地理编码和反地理编码

地理编码指 地址转经纬度,反地理编码指 经纬度转地址。使用CLGeocoder来获取。

CLGeocoder 的使用
// 创建
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
// 地理编码
[self.geocoder geocodeAddressString:地址 completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
     if (error == nil) {
     }
}];
// 反地理编码
[self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
    if (error == nil) {
    }       
}];

注意:方法中返回的是一个装着CLPlacemark对象的数组,是对输入地址名称或者经纬度进行检索的结果,因此返回多个结果供选择。error指错误信息,如果错误error有值

CLPlacemark基本属性
// 对应的位置对象 参考CLLocation基本属性
@property (nonatomic, readonly, copy, nullable) CLLocation *location;
@property (nonatomic, readonly, copy, nullable) CLRegion *region; // 范围
@property (nonatomic, readonly, copy, nullable) NSTimeZone *timeZone // 时区
@property (nonatomic, readonly, copy, nullable) NSString *name; // 地址名称
@property (nonatomic, readonly, copy, nullable) NSString *thoroughfare; // 街道
@property (nonatomic, readonly, copy, nullable) NSString *subThoroughfare; // 街道相关信息,例如门牌
@property (nonatomic, readonly, copy, nullable) NSString *locality; // 城市
@property (nonatomic, readonly, copy, nullable) NSString *subLocality; // 城市内分区
@property (nonatomic, readonly, copy, nullable) NSString *administrativeArea; // 直辖市
@property (nonatomic, readonly, copy, nullable) NSString *subAdministrativeArea; // 其他行政区域信息
@property (nonatomic, readonly, copy, nullable) NSString *postalCode; // 邮编
@property (nonatomic, readonly, copy, nullable) NSString *ISOcountryCode; // 国家编码
@property (nonatomic, readonly, copy, nullable) NSString *country; // 国家
@property (nonatomic, readonly, copy, nullable) NSString *inlandWater; // 水源湖泊
@property (nonatomic, readonly, copy, nullable) NSString *ocean; // 海洋
@property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *areasOfInterest; // 关联的或利益相关的地标

四. iOS9/iOS8/iOS8之前的定位适配

1. iOS8.0之前是默认请求授权,需要在plist文件中加入

iOS 8.0之前

设置后台执行

设置后台执行

2. iOS 8.0

使用[_locationM requestWhenInUseAuthorization]; 请求获取前台定位, [_locationM requestAlwaysAuthorization];请求获取前后台定位,并修改plist文件。

iOS8.0+请求授权

iOS8.0以上也可以在Background Modes中设置后台定位,但是当后台定位的时候,会出现一个蓝条提醒用户正在后台定位

后台定位提醒

3. iOS 9.0

iOS 9.0 与iOS8.0一样,唯一的区别在于,当在Background Modes中设置后台定位时,需要_locationM.allowsBackgroundLocationUpdates = YES;设置允许。 并且iOS 9.0中新添加了单次定位的方法[self.locationM requestLocation];只获取一次位置信息。 实现逻辑: (1) 按照定位精确度从低到高进行排序,逐个进行定位.如果在有效时间内, 定位到了精确度最好的位置, 那么就把对应的位置通过代理告知外界. (2) 如果获取到的位置不是精确度最高的那个,也会在定位超时后,通过代理告诉外界. 注意事项: (1) 必须实现代理的-locationManager:didFailWithError:方法 (2) 不能与startUpdatingLocation方法同时使用

五. 第三方框架LocationManager

第三方框架的使用非常简单,GitHub上已经讲解的很清晰。LocationManager是将CLLocationManager由代理向block的封装转换。CoreCLLocation使用代理,代码比较分散,第三方框架使用block来接收用户信息,并且额外增加了设置超时时间等功能,使用更简单方便易读。


文中如果有不对的地方欢迎指出。我是xx_cc,一只长大很久但还没有二够的家伙。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小程序之家

如何在小程序中使用罗盘

现代生活中,经常会用到导航系统,导航系统必不可少的一个功能就是罗盘,何为罗盘?简单罗盘通过磁力将内部的指针指向某个方向,从而实现判别方位。现代技术以及将罗盘缩小...

65750
来自专栏我的博客

JQuery表格表单操作

1、多选框应用代码示例 <form action=”#” method=”post”> 你喜欢的明星是?<br /> <input type=”checkbox...

31340
来自专栏携程技术中心

干货 | React Fiber 初探

21720
来自专栏nimomeng的自我进阶

Event官方文档

当系统传递一个touch event,首先会send到一个特定的view。对于touch view来讲,这个view就是被hitTest:withEve...

13720
来自专栏我的小碗汤

极致简洁的markdown编辑神器

Markdown 其实向来是文字爱好者和码农们的小众需求,市面上也涌现出了形形色色的 Markdown 编辑器,Mou、Typed、Ulysess、Macdow...

29350
来自专栏青玉伏案

iOS开发针对对Masonry下的FPS优化讨论

今天博客的内容就系统的讨论一下Masonry对FSP的影响,以及如何更好的使用Masonry。如果你对iOS开发足够熟悉的话,那么对Masonry框架应该不陌生...

28060
来自专栏有趣的django

博客园美化终极版-(自定义导航栏)----什么CSDN、简书、腾讯云专栏、个人博客和微信公众号都弱爆了

80300
来自专栏Golang语言社区

转--在学Go语言

开始学点儿Go语言,这语言据说在国内比在国外火,社区上褒贬不一,不过“小马过河”嘛,总要先自己试试再来下结论。 环境准备: 1.在Golang中国下载安装go语...

45070
来自专栏Python中文社区

用Python爬取东方财富网上市公司财务报表

摘要: 现在很多网页都采取JavaScript进行动态渲染,其中包括Ajax技术。有的网页虽然也用Ajax技术,但接口参数可能是加密的无法直接获得,比如淘宝;有...

2.5K30
来自专栏听雨堂

从MapX到MapXtreme2004[2]-图层操作

Mapx中基本的图层操作还是比较简单的,集中在对Layers和Layer的处理上,对别的没有太多要求。   在MapXtreme中,要完成类似功能,发生了一点...

23480

扫码关注云+社区

领取腾讯云代金券