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

打印輸出ios

<+37.32462634,-122.02381665> +/- 5.00m (speed 3.51 mps / course 267.11) @ 16/5/10 中國標準時間 上午8:42:58
... ...

[_locationManger requestAlwaysAuthorization]若是你的info.plist 中沒有配置相關的key。這行代碼什麼都不會作的。git

適配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

// 提醒給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 - CLLocationManagerDelegateide

#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

掉用this

//
//  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
相關文章
相關標籤/搜索