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
posted @ 2016-05-10 10:49  人生路1/5  阅读(447)  评论(0编辑  收藏  举报