9.3 方向監測
擁有GPS硬件的設備可以生成設備的當前方向(course屬性)和速度信息。iPhone設備攜帶的定位管理器可以返回一個已經計算好的course值,通過這個值我們可以獲得當前前進的方向,course值是0~360之間的浮點數,0°值表示正北方向,90°值表示正東方向,180°值表示正南方向,270°值表示正西方向,程序可以通過course值來檢測用戶位置的移動方向。除此之外,還可以通過磁力計來獲取設備的真實方向。
提示:
地球是一個大磁場,磁力計的北極將會永遠真實地指向北方。
iOS系統通過heading屬性來獲取設備的真實方向。需要指出的是,並不是所有的iOS設備都支持heading屬性,從iPhone 3gs開始引入了磁力計,因此程序在獲取方向之前需要先測試該設備是否支持heading。如果定位管理器支持heading屬性,那麼CLLocationManager的headingAvailable屬性將會返回“YES”。使用CLLocationManager獲取設備方向與獲取移動距離的步驟基本相似,只是此時不是檢測位置移動,而是檢測方向改變。
使用CLLocationManager獲取設備方向的步驟如下。
創建CLLocationManager對象,該對象負責獲取定位相關信息。併爲該對象設置一些必要的屬性。
爲CLLocationManager指定delegate屬性,該屬性值必須是一個實現CLLocationManagerDelegate協議的對象。實現CLLocationManagerDelegate協議時可根據需要實現協議中特定的方法。
調用CLLocationManager的startUpdatingHeading方法獲取方向信息。獲取方向結束時,可調用stopUpdatingHeading方法結束獲取方向信息。
當設備的方向改變時,iOS系統將會自動激發CLLocationManager的delegate對象的locationManager:didUpdateHeading:方法,而程序可通過重寫該方法來獲取設備方向。
iOS允許爲檢測方向改變設置如下屬性。
CLLocationDegrees headingFilter:設置只有當設備方向的改變值超過該屬性值時才激發delegate的方法。
CLDeviceOrientation headingOrientation:設置設備當前方向。
監聽方向時返回的是一個CLHeading對象,該對象包含如下屬性。
magneticHeading:該屬性返回設備與磁北的相對方向。
trueHeading:該屬性返回設備與真北的相對方向。 提示:真北始終指向地理北極點;磁北則對應於隨時間變化的地球磁場北極。iOS系統使用一個計算後的偏移量(稱爲偏差)來確定這兩者之間的差異。
headingAccuracy:該屬性返回方向值的誤差範圍。
timestamp:該屬性返回方向值的生成時間。
x:獲取該設備在X方向上監聽得到的原始磁力值,該磁力值的強度單位是微特斯拉。
y:獲取該設備在Y方向上監聽得到的原始磁力值,該磁力值的強度單位是微特斯拉。
z:獲取該設備在Z方向上監聽得到的原始磁力值,該磁力值的強度單位是微特斯拉。
在啓用該功能的iOS設備上,即使用戶在Settings應用中關閉了定位更新,磁向更新仍然可以使用。此外,使用heading服務的應用不會提示用戶授權問題,因此磁向信息不會泄露用戶的隱私,應用程序可以隨便使用它。
需要說明的是,trueHeading屬性需要與位置探測功能一起使用,iOS系統需要設備的位置來計算確定真北所需要的偏差。偏差隨地理位置的變化而變化,比如北京的偏差不同於東京的偏差,也不同於新加坡和馬來西亞的偏差等。有一些地方根本不能使用磁力計進行讀數。
除此之外,在某些特殊位置例如有強磁、強電干擾的地方,磁力計可能無法使用。
實例:指南針此實例將會示範如何使用磁力計來獲取設備方向,然後根據設備方向來創建一個指南針應用。新建一個Single View Application,無須修改界面設計文件,直接在應用的視圖控制器類的實現部分創建界面,並讓應用中顯示方向的圖片隨着設備方向自動旋轉即可。
下面是該應用的視圖控制器類的實現部分代碼。
程序清單:codes/09/9.3/Compass/Compass/FKViewController.m
@interface FKViewController () <CLLocationManagerDelegate> { CALayer* znzLayer; } @property (nonatomic , strong)CLLocationManager *locationManager; @end @implementation FKViewController - (void)viewDidLoad { [super viewDidLoad]; // 如果磁力計可用,則開始監聽方向改變 if([CLLocationManager headingAvailable]) { // 創建顯示方向的指南針圖片Layer znzLayer = [[CALayer alloc] init]; NSInteger screenHeight = [UIScreen mainScreen].bounds.size.height; NSInteger y = (screenHeight - 320) / 2; znzLayer.frame = CGRectMake(0 , y , 320, 320); // 設置znzLayer顯示的圖片 znzLayer.contents = (id)[[UIImage imageNamed:@"znz.png"] CGImage]; // 將znzLayer添加到系統的UIView中 [self.view.layer addSublayer:znzLayer]; // 創建CLLocationManager對象 self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; [self.locationManager startUpdatingHeading]; } // 如果磁力計不可用,則使用UIAlertView顯示提示信息 else { // 使用警告框提醒用戶 [[[UIAlertView alloc] initWithTitle:@"提醒" message:@"您的設備不支持磁力計" delegate:self cancelButtonTitle:@"確定" otherButtonTitles: nil] show]; } } // 當成功獲取設備的方向值後激發該方法 -(void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { // 將設備的方向角度換算成弧度 CGFloat headings = -1.0f * M_PI * newHeading.magneticHeading / 180.0f; // 創建不斷改變CALayer的transform屬性的屬性動畫 CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform"]; CATransform3D fromValue = znzLayer.transform; // 設置動畫開始的屬性值 anim.fromValue = [NSValue valueWithCATransform3D: fromValue]; // 繞Z軸旋轉heading弧度的變換矩陣 CATransform3D toValue = CATransform3DMakeRotation(headings , 0 , 0 , 1); // 設置動畫結束的屬性 anim.toValue = [NSValue valueWithCATransform3D: toValue]; anim.duration = 0.5; anim.removedOnCompletion = YES; // 設置動畫結束後znzLayer的變換矩陣 znzLayer.transform = toValue; // 爲znzLayer添加動畫 [znzLayer addAnimation:anim forKey:nil]; } -(BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager { return YES; } @end
上面程序中的前兩行粗體字代碼用於爲CLLocationManager設置delegate屬性,接下來程序調用該對象的startUpdatingHeading方法開始監聽設備的方向改變——當設備的方向改變時,系統會自動激發CLLocationManager設置delegate的locationManager:didUpdateHeading:方法,程序的視圖控制器重寫了該方法,並在該方法中獲取設備方向,然後將圖片“反轉”相應的角度,從而讓圖片的北極總是指向真實的北極。
編譯、運行該應用(要在真機上測試該應用,因爲iOS模擬器不支持磁力計),將可以看到如圖9.5所示的效果。
————本文節選自《瘋狂ios講義(下)》