一、定位
iOS的定位服务用到的框架是#import <CoreLocation/CoreLocation.h>
定位中用到的类是CLLocationManager

CoreLocation可以实现的功能:
1、定位功能
2、地理编码与逆地理编码

3、区域监测

二、定位核心类

1. 类方法:

+ (BOOL)locationServicesEnabled;/* 返回用户是否启用定位服务 */
+ (CLAuthorizationStatus)authorizationStatus;/* 定位服务授权状态,返回枚举类型 */

typedef NS_ENUM(int, CLAuthorizationStatus){
    kCLAuthorizationStatusNotDetermined = 0, /* 用户尚未决定是否启用定位服务 */
    kCLAuthorizationStatusRestricted, /* 没有获得用户授权 */
    kCLAuthorizationStatusDenied, /* 用户禁止使用定位或者定位服务处于关闭状态 */
    kCLAuthorizationStatusAuthorizedAlways, /* 前台、后台定位授权 */
    kCLAuthorizationStatusAuthorizedWhenInUse, /* 前台定位授权 */
};



2. 对象属性:


定位精度desiredAccuracy:


位置信息更新最小距离distanceFilter


3. 对象方法:

#pragma mark - 定位追踪
 -(void)startUpdatingLocation;/* 开始定位追踪 */
 -(void)stopUpdatingLocation;/* 停止定位追踪 */
 #pragma mark - 导航追踪
 -(void)startUpdatingHeading;/* 开始导航方向追踪 */
 -(void)stopUpdatingHeading;/* 停止导航方向追踪 */
 #pragma mark - 区域定位追踪
 -(void)startMonitoringForRegion:(CLRegion *)region;/* 开始对某个区域进行定位追踪 */
 -(void)stopMonitoringForRegion:(CLRegion *)region;/* 停止对某个区域进行定位追踪 */
 #pragma mark - 授权请求
 -(void)requestWhenInUseAuthorization;/* 请求获得应用前台定位授权 */
 -(void)requestAlwaysAuthorization;/* 请求获得应用前后台定位授权 */


4. 常用代理方法CLLocationManagerDelegate:

/* 位置发生改变后调用,第一次定位也会调用 */
 -(void)locationManager:(CLLocationManager *)manager 
     didUpdateLocations:(NSArray *)locations;
 /* 导航方向发生变化后调用 */
 -(void)locationManager:(CLLocationManager *)manager 
       didUpdateHeading:(CLHeading *)newHeading;
 /* 进入某个区域后调用 */
 -(void)locationManager:(CLLocationManager *)manager 
         didEnterRegion:(CLRegion *)region;
 /* 走出某个区域后调用 */
 -(void)locationManager:(CLLocationManager *)manager 
          didExitRegion:(CLRegion *)region;
 /* 当用户授权状态发生变化时调用 */
 -(void)locationManager:(CLLocationManager *)manager 
        didChangeAuthorizationStatus:(CLAuthorizationStatus)status;



三、示例代码

1、定位

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface ViewController ()<CLLocationManagerDelegate>
/** 位置管理者 */
@property(nonatomic,strong) CLLocationManager *mgr;
@end

@implementation ViewController

// 懒加载
- (CLLocationManager *)mgr
{
    if (_mgr == nil) {
// 实例化位置管理者
        _mgr = [[CLLocationManager alloc] init];
        // 指定代理,代理中获取位置数据
        _mgr.delegate = self;

        // 请求授权
        if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) {

            // 前台定位授权 官方文档中说明info.plist中必须有NSLocationWhenInUseUsageDescription键
            [_mgr requestWhenInUseAuthorization];
            // 前后台定位授权 官方文档中说明info.plist中必须有NSLocationAlwaysUsageDescription键
            [_mgr requestAlwaysAuthorization];
        }
    }
    return _mgr;
}

- (void)viewDidLoad {
    [super viewDidLoad];
// 开启位置更新
    [self.mgr startUpdatingLocation];

    // 设置定位所需的精度 枚举值 精确度越高越耗电
    self.mgr.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    // 每100米更新一次定位
    self.mgr.distanceFilter = 100;
}

#pragma mark - CLLocationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    NSLog(@"已经定位");
}

// 代理方法中监听授权的改变,被拒绝有两种情况,一是真正被拒绝,二是服务关闭了
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
        case kCLAuthorizationStatusNotDetermined:
        {
            NSLog(@"用户未决定");
            break;
        }
        // 系统预留字段,暂时还没用到
        case kCLAuthorizationStatusRestricted:
        {
            NSLog(@"受限制");
            break;
        }
        case kCLAuthorizationStatusDenied:
        {
            // 被拒绝有两种情况 1.设备不支持定位服务 2.定位服务被关闭
            if ([CLLocationManager locationServicesEnabled]) {
                NSLog(@"真正被拒绝");
                // 跳转到设置界面
                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;
    }
}

@end



2、地理编码


地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。

【位置 -> 地理坐标】


逆地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。

【地理坐标 -> 位置】


- (void)viewDidLoad {
    [super viewDidLoad];
    self.geocoder = [[CLGeocoder alloc] init];
    [self getCoordinateByAddress:@"北京"];
    [self getAddressByLatitude:39.54 longitude:116.28];
}

#pragma mark 根据地名确定地理坐标
-(void)getCoordinateByAddress:(NSString *)address{
    //地理编码
    [self.geocoder geocodeAddressString:address 
                      completionHandler:^(NSArray *placemarks, NSError *error) {
        //取得第一个地标,地标中存储了详细的地址信息,注意:一个地名可能搜索出多个地址
        CLPlacemark *placemark = [placemarks firstObject];
        CLLocation *location = placemark.location;//位置
        CLRegion *region = placemark.region;//区域
        NSDictionary *addressDic = placemark.addressDictionary;//详细地址信息字典
        NSLog(@"位置:%@,区域:%@,详细信息:%@",location,region,addressDic);
    }];
}
#pragma mark 根据坐标取得地名
-(void)getAddressByLatitude:(CLLocationDegrees)latitude 
                  longitude:(CLLocationDegrees)longitude
{
    //反地理编码
    CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude 
                                                      longitude:longitude];
    [self.geocoder reverseGeocodeLocation:location 
                        completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *placemark = [placemarks firstObject];
        NSLog(@"详细信息:%@",placemark.addressDictionary);
    }];
}
地标类CLPlacemark还包含以下信息:
NSString *name = placemark.name;//地名
NSString *thoroughfare = placemark.thoroughfare;//街道
NSString *subThoroughfare = placemark.subThoroughfare; //街道相关信息,例如门牌等
NSString *locality = placemark.locality; // 城市
NSString *subLocality = placemark.subLocality; // 城市相关信息,例如标志性建筑
NSString *administrativeArea = placemark.administrativeArea; // 州
NSString *subAdministrativeArea = placemark.subAdministrativeArea; //其他行政区域信息
NSString *postalCode = placemark.postalCode; //邮编
NSString *ISOcountryCode = placemark.ISOcountryCode; //国家编码
NSString *country = placemark.country; //国家
NSString *inlandWater = placemark.inlandWater; //水源、湖泊
NSString *ocean = placemark.ocean; // 海洋
NSArray *areasOfInterest = placemark.areasOfInterest; //关联的或利益相关的地标


3、区域监听

@interface ViewController ()<CLLocationManagerDelegate>
/** 位置管理者 */
@property(nonatomic,strong) CLLocationManager *mgr;
@property (weak, nonatomic) IBOutlet UILabel *msgLabel;

@end

@implementation ViewController

#pragma mark - 懒加载
- (CLLocationManager *)mgr
{
    if (_mgr == nil) {
        _mgr = [[CLLocationManager alloc] init];
        _mgr.delegate = self;
        // 区域监听,监听的是用户,所以应该让用户授权获取用户当前位置
        if ([_mgr respondsToSelector:@selector(requestAlwaysAuthorization)]) {
            [_mgr requestAlwaysAuthorization];
        }
    }
    return _mgr;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // ###细节二:判断设备是否支持区域监听(指定区域类型,一般是圆形区域)
    if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
        return;
    }

    // 0.给定一个区域
    // 0.1 区域的中点坐标
    CLLocationCoordinate2D center = CLLocationCoordinate2DMake(22.22, 33.33);
    // 0.2区域半径
    CLLocationDistance distance = 1000.0;
    // ###细节一:半径有限制
    if (distance > self.mgr.maximumRegionMonitoringDistance) {
        distance = self.mgr.maximumRegionMonitoringDistance;
    }
    CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:distance identifier:@"Chaos"];

    // 1.开启区域监听 代理中操作 -- 该方法只有用户位置发生了移动才会触发
//    [self.mgr startMonitoringForRegion:region];
    // 1.根据指定区域请求一下监听到的状态 代理中操作 -- 该方法在程序启动就会监听一下用户的位置
    // 同样当用户位置发生变化时,也会触发相应的代理方法
    [self.mgr requestStateForRegion:region];
}

#pragma mark - CLLocationManagerDelegate

/**
 *  进入指定区域后指定的代码
 *
 *  @param manager 位置管理者
 *  @param region  指定的区域
 */
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    NSLog(@"进入区域--");
    self.msgLabel.text = @"欢迎光临--";
}
/**
 *  离开指定区域后执行的代码
 *  @param manager 位置管理者
 *  @param region  指定的区域
 */
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    NSLog(@"离开区域--");
    self.msgLabel.text = @"下次再来--";
}

- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    /*
     CLRegionStateUnknown, 不知道
     CLRegionStateInside, 进入区域
     CLRegionStateOutside 离开区域
     */
    if (state == CLRegionStateInside) {

        self.msgLabel.text = @"欢迎光临";
    } else if (state == CLRegionStateOutside) {

        self.msgLabel.text = @"下次再来";
    }
}

@end