地圖(一) CoreLocation框架的使用

CoreLocation框架

因爲系統版本的不一樣,使用起來可能不一樣,下面分系統版本的不一樣進行詳細講解.

一. iOS8.0以前系統的定位

1. 前臺定位

  1. 導入CoreLocation框架以及對應的主頭文件ios

    #import <CoreLocation/CoreLocation.h>
  2. 建立CLLocationManager對象並設置代理git

    self.locationM = [[CLLocationManager alloc] init];
     self.locationM.delegate = self;
  3. 調用方法,開始更新用戶位置信息github

    [self.locationM startUpdatingLocation];
  4. 在對應的代理方法中獲取位置信息api

    -(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray<CLLocation > *)locations
     {
         NSLog(@"每當請求到位置信息時, 都會調用此方法");
     }

2. 後臺定位

  1. 在前臺定位基礎上, 勾選後臺模式Location updates

3. 額外設置

  1. 每隔多少米定位一次app

    代碼: self.locationM.distanceFilter = 100;
     功能: 只有當最新的位置與上一次獲取的位置之間的距離, 大於這個值時, 纔會經過代理告訴外界
  2. 設置定位精確度框架

    代   碼: self.locationM.desiredAccuracy = kCLLocationAccuracyBest;
     功   能: 經過設置此屬性, 獲取不一樣精確度的位置信息
     注意事項: 精確度越高,越耗電,定位所需時間越長
     枚舉註解:
    枚舉值 含義
    kCLLocationAccuracyBestForNavigation 最適合導航
    kCLLocationAccuracyBest 精度最好的
    kCLLocationAccuracyNearestTenMeters 附近10米
    kCLLocationAccuracyHundredMeters 附近100米
    kCLLocationAccuracyKilometer 附近1000米
    kCLLocationAccuracyThreeKilometers 附近3000米

3. 知識補充

1. 定位常識
    1) 標準定位服務(基於gps/基站/wifi定位, 具體使用哪一種,蘋果有本身規則)
        > 程序關閉,就無法獲取位置
    2) 顯著的位置變化定位服務(使用基站進行定位,因此必需要求設備有電話模塊)
        > 當app被徹底關閉時,也能夠接收到位置通知,並讓app進入到後臺處理
        > 定位精度相比於上面,精度不大,因此耗電小,並且定位更新頻率依據基站密度而定

2. 應用場景
    1) 若是要求定位及時,精度較高,而且運行時間較短,可以使用標準定位;
    2) 若是長時間監控用戶位置,用戶移動速度比較快(例如打車軟件),可以使用後者

4. 測試環境:

1. XCode7.0以前版本,例如XCode6.4版本
2. 模擬器選擇iOS8.0以前的版本
    * 緣由 : XCode7.0(包含7.0)以後不支持iOS8.0以前的模擬器

5. 常見問題總結

1. 定位不到, 對應的代理方法不執行
    首先,檢查運行的模擬器是不是iOS8.0以前的系統版本
    其次,檢查模擬器是否設置位置數據
    第三,確保代碼無問題(通常都是代理沒有設置,或者位置管理器對象是局部變量,亦或是位置管理器對象沒有被強引用)
    第四,絕逼是模擬器BUG, 請重置模擬器(是重置,不是重啓)

二. iOS8.0以後定位

1. 前臺定位

  1. 導入CoreLocation框架以及對應的主頭文件ide

    #import <CoreLocation/CoreLocation.h>
  2. 建立CLLocationManager對象並設置代理測試

    self.locationM = [[CLLocationManager alloc] init];
     self.locationM.delegate = self;
  3. 請求前臺定位受權, 並在Info.Plist文件中配置Key ( Nslocationwheninuseusagedescription )動畫

    [self.locationM requestWhenInUseAuthorization];
  4. 調用方法,開始更新用戶位置信息ui

    [self.locationM startUpdatingLocation];
  5. 在對應的代理方法中獲取位置信息

    -(void)locationManager:(nonnull CLLocationManager *)manager didUpdateLocations:(nonnull NSArray<CLLocation > *)locations
     {
         NSLog(@"每當請求到位置信息時, 都會調用此方法");
     }

2. 後臺定位

方案一:
  1. 在前臺定位基礎上, 勾選後臺模式Location updates
注意:此時受權狀態若是是前臺定位, 那麼當APP退到後臺時, 屏幕頂部會出現藍條
方案二:
  1. 請求先後臺定位受權,並在info.plist文件中配置KEY ( NSLocationAlwaysUsageDescription )

    [self.locationM requestAlwaysAuthorization];

    注意:不須要勾選後臺模式, 也能夠進行後臺定位

    注意:此時受權狀態若是是先後臺定位, 那麼即便APP退到後臺時, 屏幕頂部會也不會出現藍條

3. 監聽用戶受權狀態

  1. 實現如下代理方法便可

    // 當用戶受權狀態發生變化時調用
     -(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(@"定位開啓,但被拒");
             // 在此處, 應該提醒用戶給此應用受權, 並跳轉到"設置"界面讓用戶進行受權
             // 在iOS8.0以後跳轉到"設置"界面代碼
             NSURL *settingURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
             if([[UIApplication sharedApplication] canOpenURL:settingURL])
             {
                 [[UIApplication sharedApplication] openURL:settingURL];
             }
         }else
         {
             NSLog(@"定位關閉,不可用");
         }
         break;
     }
         // 獲取先後臺定位受權
     case kCLAuthorizationStatusAuthorizedAlways:
     //  case kCLAuthorizationStatusAuthorized: // 失效,不建議使用
     {
         NSLog(@"獲取先後臺定位受權");
         break;
     }
         // 得到前臺定位受權
     case kCLAuthorizationStatusAuthorizedWhenInUse:
     {
         NSLog(@"得到前臺定位受權");
         break;
     }
     default:
         break;
     }
     }

4. 測試環境:

1. XCode版本無要求
2. 模擬器選擇iOS8.0以後的版本

5. 常見問題總結

1. 定位不到, 對應的代理方法不執行
    首先,檢查是否請求受權, 並設置了對應的KEY
    其次,檢查模擬器是否設置位置數據
    第三,確保代碼無問題(通常都是代理沒有設置,或者位置管理器對象是局部變量,亦或是位置管理器對象沒有被強引用)
    第四,絕逼是模擬器BUG, 請重置模擬器(是重置,不是重啓)

三. iOS9.0 定位補充

1. 定位變化

* 前臺定位

(同iOS8.0以後一致, 無任何變化, 都須要主動請求受權)

* 後臺定位

方案一:
  1. 在前臺定位基礎上, 勾選後臺模式Location updates, 而且設置如下屬性爲YES

    if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0)
      {
          self.locationM.allowsBackgroundLocationUpdates = YES;
      }
方案二:
  1. 請求先後臺定位受權,並在info.plist文件中配置KEY ( NSLocationAlwaysUsageDescription )

    [self.locationM requestAlwaysAuthorization];

2. 新的API

  1. 單次定位請求;

    代   碼: [self.locationM requestLocation];
     功   能: 獲取一次位置信息
     實現邏輯:
         (1) 按照定位精確度從低到高進行排序,逐個進行定位.若是在有效時間內, 定位到了精確度最好的位置, 那麼就把對應的位置經過代理告知外界.
         (2) 若是獲取到的位置不是精確度最高的那個,也會在定位超時後,經過代理告訴外界.
     注意事項:
         (1) 必須實現代理的-locationManager:didFailWithError:方法
         (2) 不能與startUpdatingLocation方法同時使用

3. 測試環境:

1. XCode版本要求7.0版本以上
2. 模擬器選擇iOS9.0以後的版本

4. 常見問題總結

1. 單次定位在模擬器上測試不出效果?
    答: 由於模擬器的位置是固定的, 因此沒法測試出效果, 請使用真機進行測試.

四. CLLocation對象詳解

1. 屬性解釋

> coordinate    : 當前位置所在的經緯度數據
    > altitude      : 海拔
    > speed         : 當前速度
    > course        : 航向(設備移動的方向, 值域範圍:0.0 ~ 359.9, 正北方向爲0.0)

2. 重要方法

代碼: - (CLLocationDistance)distanceFromLocation:(CLLocation *)location
    做用: 計算兩個位置對象之間的物理距離, 單位是(米)

3. 場景演練

1. 場景演示:打印當前用戶的行走方向,偏離角度以及對應的行走距離,
    例如:」北偏東30度方向,移動了8米」
2. 實現步驟:
    1> 獲取對應的方向偏向(例如」正東」,」東偏南」)
    2> 獲取對應的偏離角度(並判斷是不是正方向)
    3> 計算行走距離
    4> 打印信息

4. 注意事項

  1. 使用位置前, 務必判斷當前獲取的位置是否有效

    代碼: if (location.horizontalAccuracy < 0) return;
     功能: 若是水平精確度小於零, 表明雖然能夠獲取位置對象, 可是數據錯誤, 不可用

* 經驗小結


一. 定位的應用場景

1) 導航
    2) 電商APP,獲取用戶所在城市(須要與(反)地理編碼聯合使用)
    3) 數據採集用戶信息(例如,統計app使用分佈)
    4) 查找周邊(周邊好友, 周邊商家等等)

二. 開發經驗

** 因爲定位很是耗電; 因此爲了給用戶省電, 你能夠遵照如下小經驗 **

1)不須要獲取用戶位置時,必定要關閉定位服務:
    2)若是能知足項目需求,儘量的使用」監聽顯著位置變化」的定位服務(打車app)
    3)若是能夠,儘量使用低精度的desiredAccuracy
    4)若是是數據採集,(通常都是週期性的去輪詢用戶位置),在輪詢期間必定要關閉定位

五. 指南針效果實現

1. 實現思路

  1. 利用"磁力計"傳感器,獲取設備朝向
  2. 根據設備朝向反向旋轉"指南針"圖片

2. 代碼實現

  1. 獲取設備朝向

    1) 導入CoreLocation框架以及對應的主頭文件
         #import <CoreLocation/CoreLocation.h>
    
     2) 建立CLLocationManager對象並設置代理
         self.locationM = [[CLLocationManager alloc] init];
         self.locationM.delegate = self;
    
     3) 調用方法, 開始獲取設備朝向
         [self.locationM startUpdatingHeading];
    
     4) 在對應的代理方法中獲取設備朝向信息
         -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
         {
             // 旋轉圖片代碼
         }
  2. 旋轉圖片

    // 1.判斷當前的角度是否有效(若是此值小於0,表明角度無效)
     if(newHeading.headingAccuracy < 0)
     return;
    
     // 2.獲取當前設備朝向(磁北方向)
     CGFloat angle = newHeading.magneticHeading;
    
     // 3.轉換成爲弧度
     CGFloat radian = angle / 180.0 * M_PI;
    
     // 4.帶動畫反向旋轉指南針
     [UIView animateWithDuration:0.5 animations:^{
         self.compassView.transform = CGAffineTransformMakeRotation(-radian);
     }];

3. 概念補充

磁北角度: newHeading.magneticHeading   ------- 相對於"磁北方向"產生的角度
    真北角度: newHeading.trueHeading           ------- 相對於"真北方向"產生的角度

4. 注意事項

1. 獲取設備朝向前, 先判斷"磁力計"是否可用
        [CLLocationManager headingAvailable];

    2. 獲取朝向前, 判斷當前朝向信息是否有效
        if(newHeading.headingAccuracy < 0) return;

    3. 注意與"航向"的區別
        設備朝向是指手機的朝向; "航向"能夠理解爲設備的移動方向

    4. 使用"磁力計"傳感器獲取設備朝向, 不須要請求用戶受權
        由於設備朝向不涉及用戶隱私

5.測試環境

1. XCode版本無要求(建議:XCode7.0不須要開發者帳號也能夠進行真機調試)
    2. 必需要求真機設備(只有真機設備纔有"磁力計"傳感器)

六. 區域監聽

1.概念解釋

區   域 :  就是指劃定的一塊地域範圍(好比圓形區域, 則由區域中心, 和半徑組成)
    區域監聽 : 是指,咱們經過代碼指定一個區域, 而後當用戶持握設備進入或者離開指定區域, 咱們都能監聽到.

2. 監聽指定區域

  1. 導入CoreLocation框架以及對應的主頭文件

    #import <CoreLocation/CoreLocation.h>
  2. 建立CLLocationManager對象並設置代理

    self.locationM = [[CLLocationManager alloc] init];
     self.locationM.delegate = self;
  3. 請求先後臺定位, 或前臺定位受權, 並在Info.Plist文件中配置相應的Key

    [self.locationM requestAlwaysAuthorization];
     // [self.locationM requestWhenInUseAuthorization];
  4. 建立一個區域, 並開始監聽

    // 1. 判斷區域監聽服務是否可用(定位服務是否關閉, 定位是否受權, 是否開啓飛行模式)
     if ([CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]])
     {
    
         // 建立區域中心
         CLLocationCoordinate2D center = CLLocationCoordinate2DMake(29.12345, 131.23456);
    
         // 建立區域(指定區域中心,和區域半徑)
         CLLocationDistance radius = 1000;
    
         // 判斷區域半徑是否大於最大監聽區域半徑,若是大於, 就無法監聽
         if (radius > self.locationM.maximumRegionMonitoringDistance) {
             radius = self.locationM.maximumRegionMonitoringDistance;
         }
         CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center radius:radius identifier:@"江哥"];
    
         // 開始監聽指定區域(只有用戶有進入區域, 或者離開區域動做的時候 纔會經過代理告訴外界)
         // 例:當一開始在區域內,不會通知代理,只有進入或離開區域時纔會調用
         [self.locationM startMonitoringForRegion:region];
    
         // 請求某個區域的狀態
         // 不止能夠獲取到指定區域的狀態, 並且當狀態發生變化時, 也會調用對應的代理方法, 告訴咱們
         [self.locationM requestStateForRegion:region];
    
     }
     else
     {
         NSLog(@"區域監聽不可用");
     }
  5. 在對應的代理方法中監聽區域狀態

    // 進去監聽區域後調用(調用一次)
     -(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);
     }
    
     /**
      *  當前請求指定區域狀態時, 回調的代理方法
      *  @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 = @"歡迎下次再來";
         }
    
     }

3. 獲取某個區域的當前狀態

監聽某個區域時, 只有進入或者離開這個區域時, 才能回調對應的方法, 是一個進入或者離開的動做
若是想知道某一個區域的當前狀態(識別用戶是在區域內部, 仍是區域外部), 則須要使用如下方法

代   碼:
    [self.locationM requestStateForRegion:region];
回調代理:
    // 請求某個區域狀態時, 回調的代理方法
    -(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
    switch (state) {
        case CLRegionStateUnknown:
            NSLog(@"未知狀態");
            break;
        case CLRegionStateInside:
            NSLog(@"在區域內部");
            break;
        case CLRegionStateOutside:
            NSLog(@"在區域外部");
            break;
        default:
         break;
    }
}

4. 測試環境

XCode版本無要求
iOS模擬器版本無要求

5. 注意事項

1. 想要作區域監聽, 在iOS8.0以後, 必須請求位置受權
   代碼: [self.locationM requestAlwaysAuthorization];
   緣由: 區域監聽的原理就是獲取用戶的位置, 而後在判斷該位置是否在制定區域內, 因此會涉及到用戶隱私(位置), 而在iOS8.0以後, 想要訪問用戶位置信息, 就須要主動請求受權;

2. 使用前, 先判斷區域監聽是否可用
   代碼:  [CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]

3. 注意區域半徑是否大於最大區域監聽半徑(若是大於, 則沒法監聽成功)
   代碼: radius > self.locationM.maximumRegionMonitoringDistance

6. 常見問題

1. 區域監聽, 測試沒有效果?
   首先, 肯定代碼沒有問題, 是否有請求受權;
   其次, 嘗試修改模擬器位置信息, 觸發進入區域或離開區域的動做
   第三, 若是模擬器出現BUG, 定位不到, 也會沒法斷定當前區域狀態; 因此, 最後能夠嘗試重置模擬器.

七. (反)地理編碼

1. 概念解釋

地理編碼:   是指根據地址關鍵字, 將其轉換成爲對應的經緯度等信息;
發地理編碼:  是指根據經緯度信息, 將其轉換成爲對應的省市區街道等信息;

2. 地理編碼

  1. 導入CoreLocation框架以及對應的主頭文件

    #import <CoreLocation/CoreLocation.h>
  2. 建立CLGeocoder對象

    self.geoC = [[CLGeocoder alloc] init];
  3. 根據地址關鍵字, 進行地理編碼

    // 直接根據地址進行地理編碼(返回結果可能有多個,由於一個地點有重名)
     [self.geoC geocodeAddressString:@"上海" completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error)
     {
         // 包含區,街道等信息的地標對象
         CLPlacemark *placemark = [placemarks firstObject];
         // 城市名稱
         NSString *city = placemark.locality;
         // 街道名稱
         NSString *street = placemark.thoroughfare;
         // 全稱
         NSString *name = placemark.name;
     }];

3. 反地理編碼

  1. 導入CoreLocation框架以及對應的主頭文件

    #import <CoreLocation/CoreLocation.h>
  2. 建立CLGeocoder對象

    self.geoC = [[CLGeocoder alloc] init];
  3. 根據經緯度信息, 進行反地理編碼

    // 根據經緯度信息進行反地理編碼
     [self.geoC reverseGeocodeLocation:[[CLLocation alloc] initWithLatitude:21.123 longitude:123.345] completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error)
     {
         // 包含區,街道等信息的地標對象
         CLPlacemark *placemark = [placemarks firstObject];
         // 城市名稱
         NSString *city = placemark.locality;
         // 街道名稱
         NSString *street = placemark.thoroughfare;
         // 全稱
         NSString *name = placemark.name;
     }];

4. CLPlacemark 地標對象詳解

location            : CLLocation 類型, 位置對象信息, 裏面包含經緯度, 海拔等等
    region              : CLRegion 類型, 地標對象對應的區域
    addressDictionary  : NSDictionary 類型, 存放街道,省市等信息
    name                : NSString 類型, 地址全稱
    thoroughfare        : NSString 類型, 街道名稱
    locality            : NSString 類型, 城市名稱
    administrativeArea : NSString 類型, 省名稱
    country             : NSString 類型, 國家名稱

5. 測試環境

*   必須聯網
    XCode版本不限
    iOS模擬器系統版本不限

6. 常見問題

1. 測試無數據?
   首先, 檢查是否有聯網;
   其次, 若是是反地理編碼,可嘗試更換經緯度再次嘗試, 有的經緯度沒有對應信息

7. 應用場景

1. 通常與定位結合使用, 肯定當前位置的具體地理信息

八. 使用第三方框架進行定位

1. 主要緣由

由於使用CoreLocation框架進行獲取用戶位置信息, 是經過代理進行回調; 而第三方框架將"代理模擬"轉換成爲"block模式"; 使用起來比較方便, 並且額外增長了超時時間等功能.

2. 框架信息

名稱: locationManager
    地址: [link](https://github.com/intuit/LocationManager)

3. 使用方法

參照該框架對應的 readME

4. 注意事項

通常集成第三方框架到項目中, 請先確保該框架沒有問題, 而後再向項目中集成

九. 補充

  1. 代理模式到block模式的轉換

    主要思想就是,先記錄下外界傳遞過來的block, 而後在對應的代理方法裏面執行這個block;
相關文章
相關標籤/搜索