天天看點

iOS-使用CoreLocation定位

要使用Core Location首先我們要先了解CLLocation、CLLocationManager、CLLocationManagerDelegate等相關概念。這些是構成我們擷取裝置定位,朝向,速度等資料的基本類。

CLLocation

  一個CLLocation對象裡存儲了由CLLocationManager對象産生的資料。其中的資料包括地理坐标(經緯度坐标)和海拔高度。使用者可以通過設定測量精度來擷取需要的資料。在iOS裝置中這個類還可以擷取裝置速度和朝向等資訊。

  通常情況下我們會使用CLLocationManager生成的執行個體來擷取裝置目前位置。然而,如果你想緩存某些定位資料和計算兩點間距離等的話,可以通過建立一個自定義的執行個體來實作。

  首先我們先來看一下這個類存儲了一些什麼資料。其中主要的包括CLLocationAccuracy、CLLocationCoordinate2D、CLLocationDegrees、CLLocationDirection、CLLocationDistance、CLLocationSpeed。要使用這些類型,都要先引入CoreLocation架構。

@import CoreLocation;
           

CLLocationAccuracy

一個類型表示測量的精度。

//最佳測量精度

extern const CLLocationAccuracy kCLLocationAccuracyBest;

//精确度十米以内

extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;

//精确度一百米以内

extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;

//精确度一千米以内

extern const CLLocationAccuracy kCLLocationAccuracyKilometer;

//精确度三千米以内

extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;

CLLocationCoordinate2D

一個包含了地理坐标(經度和緯度)的結構體。

CLLocationDegrees latitude; //緯度

CLLocationDegrees longitude; //經度

CLLocationDegrees

表示一個經度或緯度值。實際上就是一個double類型的值。

typedef double CLLocationDegrees

CLLocationDirection

表示相對于現實世界北方的方向的值。也是一個double類型的值。

typedef double CLLocationDirection

CLLocationDistance

距離值(機關:米)。double類型的值。

typedef double CLLocationDistance

CLLocationSpeed

裝置每秒移動的速度。double類型的值。

typedef double CLLocationSpeed

  了解了這個類的相關資料結構之後,我們來接着看看可以使用這個類來做一些什麼事。通過查閱穩當我們發現這個類還可以實作的最主要的功能就是計算兩點間的距離,尤其是計算設定的某一點和目前裝置位置之間的距離。

//傳回目前裝置距離設定的位置之間的距離
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
           

CLLocationManager

一個location manager對象可以提供一下相關的定位服務:

(1)追蹤使用者目前位置的變化。

(2)從羅盤中擷取朝向資訊。(隻有iOS可用)

(3)監控特定區域,當使用者進入或離開該區域時同時生成位置資訊。

(4)設定使用者位置更新在背景運作。

(5)查找附近的beacons裝置。

一下是配置一個location manage,并使用它的步驟:

(1)總是請求使用者授權定位服務,并确認需要啟用的服務正确擷取授權。

(2)建立一個CLLocationManager類的執行個體,并存儲為強引用變量。因為大多數的定位任務都是異步執行的,以局部變量的方式存儲location manager對象是非常低效的。

(3)配置設定一個特定的delegate對象。這個對象需要遵循CLLocationManagerDelegate協定。

(4)根據需要的服務配置一些額外的屬性。

(5)調用方法啟動定位服務。

擷取權限

- (void)requestWhenInUseAuthorization
           

這個方法是異步的,并且會及時的提示使用者授權定位服務的權限。另一個方法可以調用以下方法擷取權限。

- (void)requestAlwaysAuthorization
           

當使用者授權了“Always”權限後,你的App可以在前背景中使用任何的定位服務。此外,那些允許你的應用在背景執行的服務也可以繼續運作。

接下來介紹幾個常用的啟動特定服務的方法。

啟動标準定位服務

- (void)startUpdatingLocation
           

這個方法會立即傳回結果。調用這個方法會使得Location對象生成一個定位器,然後會通知委托對象,回調其

locationManager:didUpdateLocations:

消息。此後,當裝置移動的距離超過設定的distanceFilter屬性值時,接收器會再次生成一條更新消息。

- (void)stopUpdatingLocation
           

當不再需要接受定位資訊的時候可以調用這個方法。如果需要再次使用定位服務,可再次調用

startUpdatingLocation

方法。

- (void)requestLocation
           

這個方法和

startUpdatingLocation

類似,也會将擷取的定位資訊傳遞給委托對象的

locationManager:didUpdateLocations:

消息。而不同的是,這個方法隻産生一次定位資訊,在此之後定位服務就停止了。當使用這個方法時,委托對象必須要實作

locationManager:didUpdateLocations:

locationManager:didFailWithError:

方法。

後面還有類似于特定的定位服務,裝置朝向服務、區域監聽等服務,可以自行查閱開發者文檔中的說明。

CLLocationManagerDelegate

  CLLocationManagerDelegte對象是定義來接收來自CLLocation對象傳遞來的裝置定位和朝向資訊的更新的。在成功擷取定位和朝向更新資訊後,你可以在使用者互動界面進行一些更新操作。

  委托對象的方法會在你啟動定位服務的同一個線程中被調用。是以,那個線程必須是處于激活的循環狀态(active run loop),比如說應用的主線程。

  由于我們上面主要說的是定位服務,是以接下來介紹的方法中主要是和定位相關的,其他的更多方法可以查閱文檔。

- (void)locationManager:(CLLocationManager *)manager
     didUpdateLocations:(NSArray<CLLocation *> *)locations
           

當有新的可用定位資訊是會被調用。

manager

參數為産生定位資料的location對象。

locations

中存儲了傳遞過來的定位資料。這個數組通常包含至少一個定位資料。如果更新被延時或多個定位資料在被傳遞之前通知到達,則會導緻該數組中包含多個定位資訊。但是這個數組中的資料存儲是有序的,是以最新的定位資料一定是最後一個定位資料對象!!!

- (void)locationManager:(CLLocationManager *)manager
       didFailWithError:(NSError *)error
           

這個方法是可選的,但是官方文檔中推薦盡量實作該方法。

這個方法會在location對象嘗試擷取定位和朝向資訊并發生錯誤時調用。如果定位服務無法馬上擷取一個定位資訊,則會傳回一個

kCLErrorLocationUnknown

錯誤并繼續嘗試。在這種情況下,你可以簡單的忽略該錯誤資訊,并等待下一個定位資訊的到來。如果因為附近的強磁場幹擾導緻無法擷取朝向資訊,則會傳回

kCLErrorHeadingFailure

錯誤。

如果使用者拒絕你的應用使用定位服務,這個方法則會傳回

kCLErrorDenied

錯誤。如果接收到這樣一個錯誤資訊,那麼你就應該停止繼續使用定位服務。

Demon

//
    //  ViewController.m
    //  CoreLocationTest
    //
    //  Created by lysongzi on 16/1/24.
    //  Copyright © 2016年 lysongzi. All rights reserved.
    //

    #import "ViewController.h"

    #import <CoreLocation/CoreLocation.h>

    @interface ViewController () <CLLocationManagerDelegate>

    @property (strong, nonatomic) CLLocationManager *manager;

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted
            || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied)
        {
            NSLog(@"應用不可以使用定位服務.");
            return;
        }

        [self initLocation];

        //如果是IOS請求擷取權限      
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
        {
            //請求擷取使用者定位服務權限
            [self.manager requestWhenInUseAuthorization];
            NSLog(@"請求定位服務.");
        }

        //啟動定位服務
        [self.manager startUpdatingLocation];
        NSLog(@"開始定位服務.");
    }

    - (void)initLocation
    {
        //懶加載
        if (!self.manager)
        {
            self.manager = [[CLLocationManager alloc] init];
            self.manager.delegate = self;
            self.manager.desiredAccuracy = kCLLocationAccuracyBest;
            self.manager.distanceFilter = 10;
        }
    }

    #pragma mark CLLocationManagerDelegate委托實作

    -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
    {
        //擷取最新定位資訊
        CLLocation *location = [locations lastObject];

        if (location.horizontalAccuracy > 0)
        {
            NSLog(@"最新位置:%f %f, 海拔高度:%f", location.coordinate.latitude, location.coordinate.longitude, location.altitude);

            //定位成功,關閉定位服務
            [self.manager stopUpdatingLocation];
        }
    }

    -(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
    {
        if ([error code] == kCLErrorLocationUnknown) {
            NSLog(@"擷取定位失敗.");
        }
        else if ([error code] == kCLErrorHeadingFailure){
            NSLog(@"擷取朝向資訊失敗.");
        }
        else if ([error code] == kCLErrorDenied){
            NSLog(@"使用者拒絕通路定位服務.");
        }
    }

    @end           

然後我們會擷取響應的位置資訊:

2016-01-28 17:05:54.955 CoreLocationTest[460:64113] 開始定位服務.
    2016-01-28 17:05:54.972 CoreLocationTest[460:64113] 最新位置:21.478879 109.101213, 海拔高度:21.719454           

參考資料

  1. 蘋果開發者官方文檔
  2. iOS項目開發實戰——使用CoreLocation擷取目前位置資訊