CoreLocation

在移动互联网时代,移动app能解决用户的很多生活琐事,比如
周边:找餐馆、找KTV、找电影院等等
导航:根据用户设定的起点和终点,进行路线规划,并指引用户如何到达

在上述应用中,都用到了定位和地图功能,在iOS开发中,要想加入这2大功能,必须基于2个框架进行开发
CoreLocation :用于地理定位,地理编码,区域监听等(着重功能实现)
MapKit :用于地图展示,例如大头针,路线、覆盖层展示等(着重界面展示)


2个热门专业术语
LBS :Location Based Service
SoLoMo :Social Local Mobile(索罗门)

CoreLocation框架的使用
CoreLocation框架使用前提
导入框架(Xcode5.0之后可以省略)

导入主头文件
#import 

CoreLocation框架使用须知
CoreLocation框架中所有数据类型的前缀都是CL
CoreLocation中使用CLLocationManager对象来做用户定位

CLLocationManager
CLLocationManager的常用操作
开始更新用户位置
- (void)startUpdatingLocation;

停止更新用户位置
- (void) stopUpdatingLocation;

当调用了startUpdatingLocation方法后,就开始不断地请求、刷新用户的位置,一旦请求到用户位置就会调用代理的下面方法
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;
locations参数里面装着CLLocation对象

CLLocationManager补充
为了严谨起见,最好在使用定位功能之前判断当前应用的定位功能是否可用
CLLocationManager有个类方法可以判断当前应用的定位功能是否可用
  + (BOOL)locationServicesEnabled;

@property(assign, nonatomic) CLLocationDistance distanceFilter;
每隔多少米定位一次

@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
定位精确度(越精确就越耗电)

CLLocation
CLLocation用来表示某个位置的地理信息,比如经纬度、海拔等等
@property(readonly, nonatomic) CLLocationCoordinate2D coordinate;
经纬度

@property(readonly, nonatomic) CLLocationDistance altitude;
海拔

@property(readonly, nonatomic) CLLocationDirection course;
路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表真北方向)

@property(readonly, nonatomic) CLLocationSpeed speed;
移动速度(单位是m/s)

用- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location方法可以计算2个位置之间的距离

用户隐私的保护
从iOS 6开始,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权
要想获得用户的位置
想访问用户的通讯录、日历、相机、相册等等

当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权

用户隐私的保护
开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy - Location Usage Description)

一旦用户选择了“Don’t Allow”,意味着你的应用以后就无法使用定位功能
为了提高用户的授权概率,段子一定要写好!

iOS8.0+的定位适配
从iOS 8.0开始,苹果进一步加强了对用户隐私的保护。
当APP想访问用户的隐私信息时,系统不再自动弹出一个对话框让用户授权

解决方案:
   (1)调用iOS 8.0的API,主动请求用户授权
- (void)requestAlwaysAuthorization  // 请求允许在前后台都能获取用户位置的授权
- (void)requestWhenInUseAuthorization // 请求允许在前台获取用户位置的授权

(2)务必在info.plist文件中配置对应的键值, 否则以上请求授权的方法不生效
NSLocationAlwaysUsageDescription : 允许在前后台获取GPS的描述
NSLocationWhenInUseDescription  : 允许在前台获取GPS的描述

iOS9.0 定位补充
iOS 9.0 如果当前处于前台授权状态,默认是不可以后台获取用户位置。但可以设置以下属性为YES,就可以继续获取后台位置,但是会出现蓝条
@property(assign, nonatomic) BOOL allowsBackgroundLocationUpdates
使用注意:必须设置对应的后台模式:location updates

iOS 9.0 可以单次请求用户位置
- (void)requestLocation  
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray *)locations  // 成功调用
-(void)locationManager:(nonnull CLLocationManager *)manager didFailWithError:(nonnull NSError *)error // 失败调用

CLLocationCoordinate2D
CLLocationCoordinate2D是一个用来表示经纬度的结构体,定义如下
typedef struct {
        CLLocationDegrees latitude; // 纬度
        CLLocationDegrees longitude; // 经度
} CLLocationCoordinate2D;

一般用CLLocationCoordinate2DMake函数来创建CLLocationCoordinate2D
本初子午线
穿过英国伦敦格林文治天文台
往东边(右边)走,是东经(E)
往西边(左边)走,是西经(W)
东西经各180°,总共360°

赤道
往北边(上边)走,是北纬(N)
往南边(下边)走,是南纬(S)
南北纬各90°,总共180°

横跨经度\纬度越大(1° ≈ 111km)
表示的范围就越大
在地图上看到的东西就越小
天朝的经纬度范围
纬度范围:N 3°51′ ~  N 53°33′
经度范围:E 73°33′ ~  E 135°05

CLGeocoder
使用CLGeocoder可以完成“地理编码”和“反地理编码”
地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
反地理编码:根据给定的经纬度,获得具体的位置信息

地理编码方法
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

反地理编码方法
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;


CLGeocodeCompletionHandler
当地理\反地理编码完成时,就会调用CLGeocodeCompletionHandler
typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);
这个block传递2个参数
error :当编码出错时(比如编码不出具体的信息)有值
placemarks :里面装着CLPlacemark对象

CLPlacemark
CLPlacemark的字面意思是地标,封装详细的地址位置信息
@property (nonatomic, readonly) CLLocation *location;
地理位置

@property (nonatomic, readonly) CLRegion *region;
区域

@property (nonatomic, readonly) NSDictionary *addressDictionary;
详细的地址信息

@property (nonatomic, readonly) NSString *name;
地址名称

@property (nonatomic, readonly) NSString *locality;
城市

iOS8.0以前

手动配置infoPlist文件的key和手动勾选后台模式
或者
手动配置两个infoPlist文件的key
这两个都是一个意思

配置定位获取授权信息
配置定位授权信息.png
配置后台定位的两种方法
CoreLocation_第1张图片
直接配置后台定位.png

CoreLocation_第2张图片
从plist文件配置后台定位.png

iOS8以前代码:

#import "ViewController.h"
#import 
@interface ViewController ()
// 位置管理者
@property (nonatomic, strong) CLLocationManager *locationM;
@end
@implementation ViewController
#pragma mark - 懒加载对象,并在懒加载方法中进行部分初始化操作
/** 懒加载的好处
 *  1. 防止对象提前创建,占用内存空间
 *  2. 防止使用的时候,还没有被创建
 *  3. 防止被重复创建
 *  4. 可以在懒加载方法里面进行初始化设置
 */
- (CLLocationManager *)locationM
{
    if (!_locationM) {
        // 创建位置管理器
        _locationM = [[CLLocationManager alloc] init];
        // 设置代理
        _locationM.delegate = self;
        /** ------附加设置------- */
        // 设置定位距离过滤参数 (当上次定位和本次定位之间的距离 >= 此值时,才会调用代理通知开发者)
        _locationM.distanceFilter = 500;
        // 设置定位精度 (精确度越高,越耗电,所以需要我们根据实际情况,设定对应的精度)
        _locationM.desiredAccuracy = kCLLocationAccuracyBest;
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(nonnull NSSet *)touches withEvent:(nullable UIEvent *)event
{
    // 2.使用位置管理器进行定位
    if([CLLocationManager locationServicesEnabled])
    {
        [self.locationM startUpdatingLocation];
    }else
    {
        NSLog(@"不能定位呀");
    }
}
#pragma mark -CLLocationManagerDelegate
/**
 * 当位置管理器,获取到位置后,就会调用这样的方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray *)locations
{
    NSLog(@"已经定位到了!");
    // 定位到之后,可以调用此方法停止更新位置
    [self.locationM stopUpdatingLocation];
}
@end

iOS8.0以后有两种方式:这两种方式都需要请求授权

第一种:infoPlist文件配置key(前台) + 手动配置勾选后台
第一种方式相当于在infoPlist文件中配置了两个key
第二种:infoPlist文件配置key(前后台)
第二种方式只需要在infoPlist文件中配置了一个key
区别:第一种进入后台手机上方会有蓝条,第二种进入后台没有蓝条。
iOS8.0和iOS8.0以后
配置:


iOS8.0和iOS8.0+.png

CoreLocation_第3张图片
直接勾选后台定位.png
#import "ViewController.h"
#import 
@interface ViewController ()
// 位置管理者
@property (nonatomic, strong) CLLocationManager *locationM;
@end
@implementation ViewController
#pragma mark - 懒加载对象,并在懒加载方法中进行部分初始化操作
- (CLLocationManager *)locationM
{
    if (!_locationM) {
        // 创建位置管理器
        _locationM = [[CLLocationManager alloc] init];
        // 设置代理
        _locationM.delegate = self;
        /** ------附加设置------- */
        // 设置定位距离过滤参数 (当上次定位和本次定位之间的距离 >= 此值时,才会调用代理通知开发者)
//        _locationM.distanceFilter = 500;
        // 设置定位精度 (精确度越高,越耗电,所以需要我们根据实际情况,设定对应的精度)
        _locationM.desiredAccuracy = kCLLocationAccuracyBest;
        /** ------适配iOS8.0------- */
        // 判断系统版本
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
/**
 *  注意:如果两个授权都请求,那么先执行前面那个请求弹框,后面那个请求授权 有可能 下次被调用时,才会起作用
 *  如果,先请求的是“前后台授权”,那“前台授权”即使被调用,也不会有反应(因为“前后台授权”权限大于“前台授权”)
 *  反之,如果先请求的是“前台授权”,而且用户选中的是“允许”,那下次被调用时“前后台授权”会做出请求,但只请求一次
 *
 *  本质:1. 两个授权同时请求,先执行前面那个授权请求
 *       2. “前后台请求授权”方法,在(当前的授权状态 == 用户未选择状态 / 前台授权状态) 才会起作用
 *       3. “前台请求授权”方法,在(当前的授权状态 == 用户未选择状态) 才会起作用
 */
            // 请求前台的授权(默认当App进入前台后可以定位,后台不可定位)
            // 当后台模式添加location后,即使后台也可以获取用户位置,但会出现蓝条
//            [_locationM requestWhenInUseAuthorization];
            // 请求前后台的授权(前后台都可以获取用户位置,而且不会出现蓝条)
            [_locationM requestAlwaysAuthorization];
        }
        // 另外一种适配不同版本API的方法
//        if ([_locationM respondsToSelector:@selector(requestAlwaysAuthorization)]) {
//
//            [_locationM requestAlwaysAuthorization];
//            [_locationM requestWhenInUseAuthorization];
//        }
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(nonnull NSSet *)touches withEvent:(nullable UIEvent *)event
{
    // 2.使用位置管理器进行定位
    if([CLLocationManager locationServicesEnabled])
    {
        [self.locationM startUpdatingLocation];
    }else
    {
        NSLog(@"不能定位呀");
    }
}
#pragma mark -CLLocationManagerDelegate
/**
 *  当用户授权状态发生变化时调用
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
            // 用户还未决定
        case kCLAuthorizationStatusNotDetermined:
        {
            NSLog(@"用户还未决定");
            break;
        }
            // 问受限
        case kCLAuthorizationStatusRestricted:
        {
            NSLog(@"访问受限");
            break;
        }
            // 定位关闭时和对此APP授权为never时调用
        case kCLAuthorizationStatusDenied:
        {
            // 定位是否可用(是否支持定位或者定位是否开启)
            if([CLLocationManager locationServicesEnabled])
            {
                NSLog(@"定位开启,但被拒");
            }else
            {
                NSLog(@"定位关闭,不可用");
            }
            break;
        }
            // 获取前后台定位授权
        case kCLAuthorizationStatusAuthorizedAlways:
//        case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
        {
            NSLog(@"获取前后台定位授权");
            break;
        }
            // 获得前台定位授权
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        {
            NSLog(@"获得前台定位授权");
            break;
        }
        default:
            break;
    }
}
/**
 * 当位置管理器,获取到位置后,就会调用这样的方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray *)locations
{
    NSLog(@"已经定位到了!");
}
@end

iOS9之后
配置:
需要在iOS8的第一种方法上添加一行代码
// 位置管理器
@property (nonatomic, strong) CLLocationManager *locationM;
self.locationM.allowsBackgroundLocationUpdates = YES;
infoPlist文件配置key(前台) + 手动配置勾选后台 
相当于在infoPlist文件中配置了两个key
并且比iOS8多了一个方法,
 [self.locationM requestLocation];
能自定提高定位的精确度,因为定位精度越小,越耗时,系统会一步一步提高精度,当系统发现超时,就会将此时(或者说上次的定位值)返回给你。
而且,代理还要实现定时失败的方法。
还要注意requestLocation不能和一些方法同用,请看系统注释。

代码:

#import "ViewController.h"
#import 
@interface ViewController ()
// 位置管理器
@property (nonatomic, strong) CLLocationManager *locationM;
@end
@implementation ViewController
#pragma mark - 懒加载对象,并在懒加载方法中进行部分初始化操作
- (CLLocationManager *)locationM
{
    if (!_locationM) {
        // 创建位置管理器
        _locationM = [[CLLocationManager alloc] init];
        // 设置代理
        _locationM.delegate = self;
        // 判断系统版本,请求前台授权
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
            // 在iOS9.0+(如果当前授权状态是使用时授权,那么App退到后台后,将不能获取用户位置,即使勾选后台模式:location)
            [_locationM requestWhenInUseAuthorization];
        }
        // 要想继续获取位置,需要使用以下属性进行设置(注意勾选后台模式:location)但会出现蓝条
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
            _locationM.allowsBackgroundLocationUpdates = YES;
        }
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(nonnull NSSet *)touches withEvent:(nullable UIEvent *)event
{
    // 2.使用位置管理器进行定位
    if([CLLocationManager locationServicesEnabled])
    {
//        [self.locationM startUpdatingLocation];
        // 作用:按照定位精确度从低到高进行排序,逐个进行定位。如果获取到的位置不是精确度最高的那个,也会在定位超时后,通过代理告诉外界
        // 注意:一个要实现代理的定位失败方法; 二:不能与startUpdatingLocation同时使用
        [self.locationM requestLocation];
    }else
    {
        NSLog(@"不能定位呀");
    }
}
#pragma mark -CLLocationManagerDelegate
/**
 *  当用户授权状态发生变化时调用
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
            // 用户还未决定
        case kCLAuthorizationStatusNotDetermined:
        {
            NSLog(@"用户还未决定");
            break;
        }
            // 问受限
        case kCLAuthorizationStatusRestricted:
        {
            NSLog(@"访问受限");
            break;
        }
            // 定位关闭时和对此APP授权为never时调用
        case kCLAuthorizationStatusDenied:
        {
            // 定位是否可用(是否支持定位或者定位是否开启)
            if([CLLocationManager locationServicesEnabled])
            {
                NSLog(@"定位开启,但被拒");
            }else
            {
                NSLog(@"定位关闭,不可用");
            }
            break;
        }
            // 获取前后台定位授权
        case kCLAuthorizationStatusAuthorizedAlways:
//        case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
        {
            NSLog(@"获取前后台定位授权");
            break;
        }
            // 获得前台定位授权
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        {
            NSLog(@"获得前台定位授权");
            break;
        }
        default:
            break;
    }
}
/**
 * 当位置管理器,获取到位置后,就会调用这样的方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray *)locations
{
    NSLog(@"已经定位到了!--%@", locations);  
}
/**
 * 当定位失败后调用此方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didFailWithError:(nonnull NSError *)error
{
    NSLog(@"定位失败--%@", error.localizedDescription);
}
@end

CoreLocation框架的基本实现

在9.0上修改

#import "ViewController.h"
#import 
@interface ViewController ()
// 位置管理者
@property (nonatomic, strong) CLLocationManager *locationM;
@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];
        }
        // iOS9.0 以前,使用后台定位没有提示,从iOS9.0开始, 如果使用后台定位没有调用此API,屏幕会出现蓝条(类似热点开启)
        // 注意设置对应的后台运行模式,否则会崩溃
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
            _locationM.allowsBackgroundLocationUpdates = YES;
        }
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(nonnull NSSet *)touches withEvent:(nullable UIEvent *)event
{
    // 2.使用位置管理器进行定位
    if([CLLocationManager locationServicesEnabled])
    {
//        [self.locationM startUpdatingLocation];
        // 只请求一次定位位置(注意:必须实现代理的-locationManager:didFailWithError:方法)
        // 不能与startUpdatingLocation方法同时使用
        [self.locationM requestLocation];
    }else
    {
        NSLog(@"不能定位呀");
    }
}
#pragma mark -CLLocationManagerDelegate
/**
 *  当用户授权状态发生变化时调用
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
            // 用户还未决定
        case kCLAuthorizationStatusNotDetermined:
        {
            NSLog(@"用户还未决定");
            break;
        }
            // 问受限
        case kCLAuthorizationStatusRestricted:
        {
            NSLog(@"访问受限");
            break;
        }
            // 定位关闭时和对此APP授权为never时调用
        case kCLAuthorizationStatusDenied:
        {
            // 定位是否可用(是否支持定位或者定位是否开启)
            if([CLLocationManager locationServicesEnabled])
            {
                NSLog(@"定位开启,但被拒");
            }else
            {
                NSLog(@"定位关闭,不可用");
            }
            break;
        }
            // 获取前后台定位授权
        case kCLAuthorizationStatusAuthorizedAlways:
//        case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
        {
            NSLog(@"获取前后台定位授权");
            break;
        }
            // 获得前台定位授权
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        {
            NSLog(@"获得前台定位授权");
            break;
        }
        default:
            break;
    }
}
/**
 * 当位置管理器,获取到位置后,就会调用这样的方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray *)locations
{
    CLLocation *location = [locations firstObject];
    NSLog(@"%@", location);
}
/**
 * 当定位失败后调用此方法
 */
-(void)locationManager:(nonnull CLLocationManager *)manager didFailWithError:(nonnull NSError *)error
{
    NSLog(@"定位失败--%@", error.localizedDescription);
}
@end

指南针
#import "ViewController.h"
#import 
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *compassView;
@property (nonatomic, strong) CLLocationManager *locationM;
@end
@implementation ViewController
#pragma mark - 懒加载
-(CLLocationManager *)locationM
{
    if (!_locationM) {
        /**
         *  注意:获取手机设备朝向,不需要用户授权
         */
        _locationM = [[CLLocationManager alloc] init];
        _locationM.delegate = self;
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    if ([CLLocationManager headingAvailable]) {
        // 开始监听设备朝向
        [self.locationM startUpdatingHeading];
    }
}
#pragma mark - CLLocationManagerDelegate
-(void)locationManager:(nonnull CLLocationManager *)manager didUpdateHeading:(nonnull CLHeading *)newHeading
{
    // 获取当前设备朝向(磁北方向)
    CGFloat angle = newHeading.magneticHeading;
    // 转换成为弧度
    CGFloat radian = angle / 180.0 * M_PI;
    // 反向旋转指南针
    [UIView animateWithDuration:0.5 animations:^{
        self.compassView.transform = CGAffineTransformMakeRotation(-radian);
    }];
}
@end

区域监听
#import "ViewController.h"
#import 
@interface ViewController ()
@property (nonatomic, strong) CLLocationManager *locationM;
@end
@implementation ViewController
#pragma mark - 懒加载
-(CLLocationManager *)locationM
{
    if (!_locationM) {
        _locationM = [[CLLocationManager alloc] init];
        _locationM.delegate = self;
        if ([_locationM respondsToSelector:@selector(requestAlwaysAuthorization)]) {
            [_locationM requestAlwaysAuthorization];
        }
    }
    return _locationM;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建区域中心
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake(29.12345, 131.23456);
    // 创建区域(指定区域中心,和区域半径)
    CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:1000 identifier:@"家里"];
    // 开始监听指定区域
    [self.locationM startMonitoringForRegion:region];
}
#pragma mark - CLLocationManagerDelegate
// 进去监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didEnterRegion:(nonnull CLRegion *)region
{
    NSLog(@"进入区域---%@", region.identifier);
    [manager stopMonitoringForRegion:region];
}
// 离开监听区域后调用(调用一次)
-(void)locationManager:(nonnull CLLocationManager *)manager didExitRegion:(nonnull CLRegion *)region
{
    NSLog(@"离开区域---%@", region.identifier);
}
@end

地理编码
#import "ViewController.h"
#import 
#import 
@interface ViewController ()
// 地址详情TextView
@property (weak, nonatomic) IBOutlet UITextView *addressDetailTV;
// 纬度TextField
@property (weak, nonatomic) IBOutlet UITextField *latitudeTF;
// 经度度TextField
@property (weak, nonatomic) IBOutlet UITextField *longtitudeTF;
// 用作地理编码、反地理编码的工具类
@property (nonatomic, strong) CLGeocoder *geoC;
@end
@implementation ViewController
#pragma mark - 懒加载
-(CLGeocoder *)geoC
{
    if (!_geoC) {
        _geoC = [[CLGeocoder alloc] init];
    }
    return _geoC;
}
// 地理编码
- (IBAction)geoCoder {
    if ([self.addressDetailTV.text length] == 0) {
        return;
    }
    // 地理编码方案一:直接根据地址进行地理编码(返回结果可能有多个,因为一个地点有重名)
    [self.geoC geocodeAddressString:self.addressDetailTV.text completionHandler:^(NSArray * __nullable placemarks, NSError * __nullable error) {
        // 包含区,街道等信息的地标对象
        CLPlacemark *placemark = [placemarks firstObject];
        // 城市名称
//        NSString *city = placemark.locality;
        // 街道名称
//        NSString *street = placemark.thoroughfare;
        // 全称
        NSString *name = placemark.name;
        self.addressDetailTV.text = [NSString stringWithFormat:@"%@", name];
        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
    }];
    // 地理编码方案二:根据地址和区域两个条件进行地理编码(更加精确)
//    [self.geoC geocodeAddressString:self.addressDetailTV.text inRegion:nil completionHandler:^(NSArray * __nullable placemarks, NSError * __nullable error) {
//        // 包含区,街道等信息的地标对象
//        CLPlacemark *placemark = [placemarks firstObject];
//        self.addressDetailTV.text = placemark.description;
//        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
//        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
//    }];
    // 地理编码方案三:
//    NSDictionary *addressDic = @{
//                                 (__bridge NSString *)kABPersonAddressCityKey : @"北京",
//                                 (__bridge NSString *)kABPersonAddressStreetKey : @"棠下街"
//                                 };
//    [self.geoC geocodeAddressDictionary:addressDic completionHandler:^(NSArray * __nullable placemarks, NSError * __nullable error) {
//        CLPlacemark *placemark = [placemarks firstObject];
//        self.addressDetailTV.text = placemark.description;
//        self.latitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.latitude];
//        self.longtitudeTF.text = [NSString stringWithFormat:@"%f", placemark.location.coordinate.longitude];
//    }];
}
// 反地理编码
- (IBAction)decode {
    // 过滤空数据
    if ([self.latitudeTF.text length] == 0 || [self.longtitudeTF.text length] == 0) {
        return;
    }
    // 创建CLLocation对象
    CLLocation *location = [[CLLocation alloc] initWithLatitude:[self.latitudeTF.text doubleValue] longitude:[self.longtitudeTF.text doubleValue]];
    // 根据CLLocation对象进行反地理编码
    [self.geoC reverseGeocodeLocation:location completionHandler:^(NSArray * __nullable placemarks, NSError * __nullable error) {
        // 包含区,街道等信息的地标对象
        CLPlacemark *placemark = [placemarks firstObject];
        // 城市名称
//        NSString *city = placemark.locality;
        // 街道名称
//        NSString *street = placemark.thoroughfare;
        // 全称
        NSString *name = placemark.name;
        self.addressDetailTV.text = [NSString stringWithFormat:@"%@", name];
    }];
}
@end

你可能感兴趣的:(CoreLocation)