瘋狂ios講義之使用CoreLocation定位(2)

9.2獲取定位信息


iOS開發者使用CoreLocation.framework框架進行定位非常簡單CoreLocation框架的常用API主要有如下幾個。

CLLocationManager定位管理器類。

CLLocationManagerdelegate該協議代表定位管理器的delegate協議。實現該協議的對象可負責處理CLLocationManager的定位事件。

CLLocation該對象代表位置。該對象包含了當前設備的經度、緯度、高度、速度、路線等信息還包含了該定位信息的水平精確度、垂直精確度以及時間戳信息。

CLHeading該對象代表設備的移動方向。

CLRegion該對象代表一個區域。一般程序不會直接使用該類而是使用它的兩個子類即CLCircularRegion圓形區域和CLBeaconRegion藍牙信號區。

除此之外CoreLocation框架還涉及一個CLLocationCoordinate2D結構體變量該結構體變量包含經度、緯度兩個值。其中CLLocation對象的coordinate屬性就是一個CLLocationCoordinate2D結構體變量。

瞭解CoreLocation提供的這些API之後接下來即可通過這些API進行定位了。

9.2.1 獲取位置信息

使用CoreLocation.framework進行定位只要如下3步即可。

創建CLLocationManager對象該對象負責獲取定位相關信息。併爲該對象設置一些必要的屬性。

CLLocationManager指定delegate屬性該屬性值必須是一個實現CLLocationManagerDelegate協議的對象。實現CLLocationManagerDelegate協議時可根據需要實現協議中特定的方法。

調用CLLocationManagerstartUpdatingLocation方法獲取定位信息。定位結束時可調用stopUpdatingLocation方法結束獲取定位信息。

注意    

    爲了在iOS應用中使用CoreLocation.framework需要完成兩件事情①爲應用添加CoreLocation.framework框架②在需要使用定位服務及相關類的源文件中使用“#import <CoreLocation/CoreLocation.h>”導入CoreLocation.framework的頭文件。本章絕大部分示例都使用了CoreLocation.framework因此都需要執行上面兩步操作。

從上面介紹不難看出使用CoreLocation進行定位的關鍵就是CLLocationManager對象及其delegate對象。其中CLLocationManager負責獲取定位信息而delegate則負責處理定位事件——通過這些事件即可獲取設備所在位置。

CLLocationManager還提供瞭如下類方法來判斷當前設備的定位相關服務狀態。

+ locationServicesEnabled返回當前定位服務是否可用。

+ deferredLocationUpdatesAvailable返回延遲定位更新是否可用。

+ significantLocationChangeMonitoringAvailable返回重大位置改變監聽是否可用。

+ headingAvailable返回該設備是否支持磁力計計算方向。

+ isRangingAvailable返回藍牙信號範圍服務是否可用。這是iOS 7新增的方法。

除此之外在使用CLLocationManager開始定位之前還可爲該對象設置如下屬性。

pausesLocationUpdatesAutomatically設置iOS設備是否可暫停定位來節省電池的電量。如果該屬性設爲“YES”則當iOS設備不再需要定位數據時iOS設備可以自動暫停定位。

distanceFilter設置CLLocationManager的自動過濾距離。也就是說只有當設備在水平方向的位置改變超過該數值以米爲單位指定的距離時纔會生成一次位置改變的信號。

desiredAccuracy設置定位服務的精度。該屬性值支持kCLLocationAccuracyBestForNavigation導航級的最佳精確度、kCLLocationAccuracyBest最佳精確度、kCLLocationAccuracy NearestTenMeters10米誤差、kCLLocationAccuracyHundredMeters百米誤差、kCLLocationAccuracyKilometer千米誤差、kCLLocationAccuracyThreeKilometers三千米誤差等常量值。當然也可直接指定一個浮點數作爲定位服務允許的誤差。

activityType設置定位數據的用途。該屬性支持CLActivityTypeOther定位數據作爲普通用途、CLActivityTypeAutomotiveNavigation定位數據作爲車輛導航使用、CLActivityTypeFitness定位數據作爲步行導航使用和CLActivityTypeOtherNavigation定位數據作爲其他導航使用這幾個枚舉值之一。

接下來通過示例來示範使用CoreLocation定位iOS設備的位置。

創建一個Single View Application該項目包含一個應用程序委託類、一個視圖控制器類和Main.storyboard界面設計文件。打開該項目的界面設計文件向其中添加5個文本框分別用於顯示當前設備的經度、緯度、高度、速度和方向並在界面上添加一個UIButton按鈕。

爲了在程序中訪問界面上的5個文本框需要將它們分別綁定到視圖控制器類的longitudeTxtlatitudeTxtaltitudeTxtspeedTxtcourseTxt5IBOutlet屬性爲了讓程序能響應按鈕的點擊事件還需要爲按鈕的“Touch Up Inside”事件綁定bnTapped:事件處理方法。

下面是該視圖控制器類的實現部分代碼。

程序清單codes/09/9.2/LocationTest/LocationTest/FKViewController.m

@interface FKViewController () <CLLocationManagerDelegate>
@property (strong,nonatomic)CLLocationManager *locationManager;
@end
@implementation FKViewController
- (void)viewDidLoad
{  
    [super viewDidLoad];
    // 創建CLLocationManager對象
    self.locationManager = [[CLLocationManager alloc]init];
}
- (IBAction)bnTapped:(id)sender
{
    // 如果定位服務可用
    if([CLLocationManager locationServicesEnabled])
    {
        NSLog( @"開始執行定位服務" );
        // 設置定位精度最佳精度
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        // 設置距離過濾器爲50米表示每移動50米更新一次位置
        self.locationManager.distanceFilter = 50;
        // 將視圖控制器自身設置爲CLLocationManager的delegate
        // 因此該視圖控制器需要實現CLLocationManagerDelegate協議
        self.locationManager.delegate = self;
        // 開始監聽定位信息
        [self.locationManager startUpdatingLocation];
    }
    else
    {
        NSLog( @"無法使用定位服務" );
    }
}
// 成功獲取定位數據後將會激發該方法
-(void)locationManager:(CLLocationManager *)manager
    didUpdateLocations:(NSArray *)locations
{
    // 獲取最後一個定位數據
    CLLocation* location = [locations lastObject];
    // 依次獲取CLLocation中封裝的經度、緯度、高度、速度、方向等信息
    self.latitudeTxt.text = [NSString stringWithFormat:@"%g",
        location.coordinate.latitude];
    self.longitudeTxt.text = [NSString stringWithFormat:@"%g",
        location.coordinate.longitude];
    self.altitudeTxt.text = [NSString stringWithFormat:@"%g",
        location.altitude];
    self.speedTxt.text = [NSString stringWithFormat:@"%g",
        location.speed];
    self.courseTxt.text = [NSString stringWithFormat:@"%g",
        location.course];
}
// 定位失敗時激發的方法
- (void)locationManager:(CLLocationManager *)manager
    didFailWithError:(NSError *)error
{
    NSLog(@"定位失敗: %@",error);
}
@end

上面程序中的第1段粗體字代碼爲CLLocationManager對象設置了屬性之後關鍵是將該視圖控制器設置爲CLLocationManagerdelegate程序調用CLLocationManagerstartUpdatingLocation方法開始獲取定位數據。

由於程序指定該視圖控制器作爲CLLocationManagerdelegate因此該視圖控制器需要實現CLLocationManagerDelegate協議並實現該協議中定位相關的兩個事件處理方法。當程序成功獲取定位數據時將會激發delegatelocationManager:didUpdateLocations:方法因此上面視圖控制器類實現了該方法並在該方法中獲取最後一個定位數據CLLocation對象。

CLLocation對象中包含如下屬性這些屬性包含了定位相關信息。

altitude該屬性表示當前設備的海拔高度單位是米。

coordinate該屬性返回一個CLLocationCoordinate2D結構體變量該結構體變量中包含經度、緯度信息。

course該屬性表示當前設備前進的方向。該值爲表示向北90°表示向東180°表示向南270°表示向西。

horizontalAccuracy該屬性表明定位信息的水平精確度。將返回的座標作爲圓心並將水平精確度視爲半徑。真正的設備位置落在此圓內的某處。此圓越小位置就越精確此圓越大則位置越不精確。如果精確度爲負值則表明測量精確度失敗。

verticalAccuracy該屬性表明定位信息的垂直精確度。也就是說iOS設備的實際高度在該定位信息的高度加或減該屬性值的範圍內。

timestamp該屬性返回定位信息的返回時間。

speed該屬性表示返回設備的移動速度單位是米/秒。實際上該屬性適用於行車速度而不太適用於步行速度。

每個iOS應用第一次使用定位功能時都會因爲權限問題而彈出是否允許當前應用程序獲取定位操作權限的提示框如圖9.1所示。

110125_vcoW_262659.jpg

9.1 詢問用戶是否允許該應用使用定位功能

單擊“OK”按鈕即可在Xcode控制器中看到“開始執行定位服務”的提示信息但可能依然看不到程序界面上有任何輸出——此時我們可以通過模擬器來模擬設備的位置。

9.2.2使用iOS模擬器模擬位置

iOS模擬器本身並不能作爲GPS接收機因此無法得到定位信息但爲了方便程序員測試定位應用iOS模擬器可以模擬定位信息。

啓動iOS模擬器之後即可通過iOS模擬器主菜單中的“調試”→“位置”來模擬iOS設備的位置該菜單如圖9.2所示。

110204_32y4_262659.jpg

9.2所示菜單支持如下幾種位置信息。

自定位置開發者可以自行輸入位置的經度值、緯度值。

City Bicycle Ride模擬設備攜帶者在城市中騎車移動。

City Run模擬設備攜帶者在城市中跑動。

Freeway Drive模擬設備攜帶者在高速公路中駕車。

如果選擇“Freeway Driver”來模擬該設備攜帶者在高速公路中駕車則可以看到該應用顯示如圖9.3所示。

110214_7Nk5_262659.jpg

9.2.3監控行車速度和行車距離

上一個示例是通過CLLocation對象來獲取設備的移動速度和移動方向但這種移動速度屬性適用於行車速度而不太適用於步行速度。如果希望程序計算平均移動速度則只要不斷地累計設備的移動距離和移動時間再用距離除以時間即可得到設備的平均移動速度。

下面通過一個示例來計算設備的平均移動速度。新建一個Single View Application打開該應用的Main.storyboard界面設計文件向該界面設計文件中拖入一個UITextView控件用於顯示該設備的移動速度。爲了在程序中訪問該UITextView控件程序將它綁定到視圖控制器的showView控件。

接下來修改視圖控制器類在視圖控制器類中通過設備的移動距離和移動時間來計算速度。下面是該視圖控制器類的實現部分代碼。

程序清單codes/09/9.2/SpeedMonitor/SpeedMonitor/FKViewController.m

#import "FKViewController.h"
#import <CoreLocation/CoreLocation.h>
@interface FKViewController () <CLLocationManagerDelegate>
@property (nonatomic , retain) CLLocationManager *locationManager;
@property (nonatomic , retain) CLLocation *prevLocation;
@property (nonatomic , assign) CGFloat sumDistance;
@property (nonatomic , assign) CGFloat sumTime;
@end
@implementation FKViewController
- (void) viewDidLoad
{
    [super viewDidLoad];
    // 創建CLLocationManager對象
    self.locationManager = [[CLLocationManager alloc] init];
}
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // 設置定位精度最佳精度
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    // 設置距離過濾器爲50米表示每移動50米更新一次位置
    self.locationManager.distanceFilter = 50;
    // 將視圖控制器自身設置爲CLLocationManager的delegate
    // 因此該視圖控制器需要實現CLLocationManagerDelegate協議
    self.locationManager.delegate = self;
    // 開始監聽定位信息
    [self.locationManager startUpdatingLocation];
    NSLog(@"開始執行定位服務" );
}
// 定位失敗時激發的方法
- (void)locationManager:(CLLocationManager *)manager
    didFailWithError:(NSError *)error
{
    NSLog(@"定位失敗: %@",error);
}
// 成功獲取定位數據後將會激發該方法
-(void)locationManager:(CLLocationManager *)manager
    didUpdateLocations:(NSArray *)locations
{
    // 獲取最後一個定位數據
    CLLocation* newLocation = [locations lastObject];
    if(newLocation.horizontalAccuracy < kCLLocationAccuracyHundredMeters)
    {
        if(self.prevLocation)
        {
            // 計算本次定位數據與上次定位數據之間的時間差
            NSTimeInterval dTime = [newLocation.timestamp
                timeIntervalSinceDate:self.prevLocation.timestamp];
            // 累計行車時間
            self.sumTime += dTime;
            // 計算本次定位數據與上次定位數據之間的距離
            CGFloat distance = [newLocation
                distanceFromLocation:self.prevLocation];
            // 如果距離小於1米則忽略本次數據直接返回該方法
            if(distance < 1.0f)
            {
                return;
            }
            // 累加移動距離
            self.sumDistance += distance;
            // 計算移動速度將米/秒換算成千米/小時需要乘以3.6
            CGFloat speed = distance / dTime * 3.6;
            // 計算平均速度
            CGFloat avgSpeed = self.sumDistance / self.sumTime * 3.6;
            NSString * speedFeedback = [NSString stringWithFormat:
                @"當前速度爲%g千米/小時平均速度爲:%g千米/小時。合計移動:%g千米",
                speed , avgSpeed , self.sumDistance / 1000];
            self.showView.text = speedFeedback;
        }
        self.prevLocation = newLocation;
    }
}
@end

上面程序中的兩行粗體字代碼分別用於計算本次定位數據與上次定位數據之間的時間差、距離用此距離除以時間即可得到該設備的當前速度。除此之外該程序還定義了一個sumDistance屬性來保存設備移動的總距離並定義了一個sumTime來保存設備移動的總時間用設備移動的總距離除以設備移動的總時間即可獲取該設備移動的平均速度。

提示

iOS系統獲取的前後兩次定位數據的時間差以秒爲單位前後兩次定位數據之間的距離以米爲單位因此直接用距離除以時間得到速度單位爲米/秒。如果程序希望以千米/小時作爲速度單位則需要乘以3.6

編譯、運行該程序並選擇“Freeway Driver”來模擬設備攜帶者在高速公路中駕車將可以看到該應用顯示如圖9.4所示的結果。

110359_QxaG_262659.jpg

————本文節選自《瘋狂ios講義下》

110624_Bbvc_262659.jpg


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章