IOS CoreLocation實(shí)現(xiàn)系統(tǒng)自帶定位的方法
Core Location是iOS SDK中一個(gè)提供設(shè)備位置的框架??梢允褂萌N技術(shù)來(lái)獲取位置:GPS、蜂窩或WiFi。在這些技術(shù)中,GPS最為精準(zhǔn),如果有GPS硬件,Core Location將優(yōu)先使用它。如果設(shè)備沒(méi)有GPS硬件(如WiFi iPad)或使用GPS獲取當(dāng)前位置時(shí)失敗,Core Location將退而求其次,選擇使用蜂窩或WiFi。
Core Location的大多數(shù)功能是由位置管理器(CLLocationManager)提供的,可以使用位置管理器來(lái)指定位置更新的頻率和精度,以及開(kāi)始和停止接收這些更新。
要使用位置管理器,必須首先將框架Core Location加入到項(xiàng)目中,再導(dǎo)入其接口文件:
#import <CoreLocation/CoreLocation.h>
并初始化位置管理器,指定更新代理,以及一些更新設(shè)置,然后更新
CLLocationManager *locManager = [[CLLocationManager alloc] init]; locManager.delegate = self; [locManager startUpdatingLocation];
位置管理器委托(CLLocationManagerDelegate)有兩個(gè)與位置相關(guān)的方法:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *curLocation = [locations lastObject];
if(curLocation.horizontalAccuracy > 0)
{
NSLog(@"當(dāng)前位置:%.0f,%.0f +/- %.0f meters",curLocation.coordinate.longitude,
curLocation.coordinate.latitude,
curLocation.horizontalAccuracy);
}
if(curLocation.verticalAccuracy > 0)
{
NSLog(@"當(dāng)前海拔高度:%.0f +/- %.0f meters",curLocation.altitude,curLocation.verticalAccuracy);
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{ //此方法為定位失敗的時(shí)候調(diào)用。并且由于會(huì)在失敗以后重新定位,所以必須在末尾停止更新
if(error.code == kCLErrorLocationUnknown)
{
NSLog(@"Currently unable to retrieve location.");
}
else if(error.code == kCLErrorNetwork)
{
NSLog(@"Network used to retrieve location is unavailable.");
}
else if(error.code == kCLErrorDenied)
{
NSLog(@"Permission to retrieve location is denied.");
[manager stopUpdatingLocation];
}
}
第一個(gè)方法處理定位成功,manager參數(shù)表示位置管理器實(shí)例;locations為一個(gè)數(shù)組,是位置變化的集合,它按照時(shí)間變化的順序存放。如果想獲得設(shè)備的當(dāng)前位置,只需要訪問(wèn)數(shù)組的最后一個(gè)元素即可。集合中每個(gè)對(duì)象類(lèi)型是CLLocation,它包含以下屬性:
coordinate — 坐標(biāo)。一個(gè)封裝了經(jīng)度和緯度的結(jié)構(gòu)體。
altitude — 海拔高度。正數(shù)表示在海平面之上,而負(fù)數(shù)表示在海平面之下。
horizontalAccuracy — 位置的精度(半徑)。位置精度通過(guò)一個(gè)圓表示,實(shí)際位置可能位于這個(gè)圓內(nèi)的任何地方。這個(gè)圓是由coordinate(坐標(biāo))和horizontalAccuracy(半徑)共同決定的,horizontalAccuracy的值越大,那么定義的圓就越大,因此位置精度就越低。如果horizontalAccuracy的值為負(fù),則表明coordinate的值無(wú)效。
verticalAccuracy — 海拔高度的精度。為正值表示海拔高度的誤差為對(duì)應(yīng)的米數(shù);為負(fù)表示altitude(海拔高度)的值無(wú)效。
speed — 速度。該屬性是通過(guò)比較當(dāng)前位置和前一個(gè)位置,并比較它們之間的時(shí)間差異和距離計(jì)算得到的。鑒于Core Location更新的頻率,speed屬性的值不是非常精確,除非移動(dòng)速度變化很小。
應(yīng)用程序開(kāi)始跟蹤用戶(hù)的位置時(shí),將在屏幕上顯示一個(gè)是否允許定位的提示框。如果用戶(hù)禁用定位服務(wù),iOS不會(huì)禁止應(yīng)用程序運(yùn)行,但位置管理器將生成錯(cuò)誤。
第二個(gè)方法處理這種定位失敗,該方法的參數(shù)指出了失敗的原因。如果用戶(hù)禁止應(yīng)用程序定位,error參數(shù)將為kCLErrorDenied;如果Core Location經(jīng)過(guò)努力后無(wú)法確認(rèn)位置,error參數(shù)將為kCLErrorLocationUnknown;如果沒(méi)有可供獲取位置的源,error參數(shù)將為kCLErrorNetwork。
通常,Core Location將在發(fā)生錯(cuò)誤后繼續(xù)嘗試確定位置,但如果是用戶(hù)禁止定位,它就不會(huì)這樣做;在這種情況下,應(yīng)使用方法stopUpdatingLocation停止位置管理器。
可根據(jù)實(shí)際情況來(lái)指定位置精度。例如,對(duì)于只需確定用戶(hù)在哪個(gè)國(guó)家的應(yīng)用程序,沒(méi)有必要要求Core Location的精度為10米。要指定精度,可在啟動(dòng)位置更新前設(shè)置位置管理器的desiredAccuracy。有6個(gè)表示不同精度的枚舉值:
extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation; extern const CLLocationAccuracy kCLLocationAccuracyBest; extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters; extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters; extern const CLLocationAccuracy kCLLocationAccuracyKilometer; extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;
對(duì)位置管理器啟動(dòng)更新后,更新將不斷傳遞給位置管理器委托,直到停止更新。您無(wú)法直接控制這些更新的頻率,但可使用位置管理器的屬性distanceFilter進(jìn)行間接控制。在啟動(dòng)更新前設(shè)置屬性distanceFilter,它指定設(shè)備(水平或垂直)移動(dòng)多少米后才將另一個(gè)更新發(fā)送給委托。下面的代碼使用適合跟蹤長(zhǎng)途跋涉者的設(shè)置啟動(dòng)位置管理器:
CLLocationManager *locManager = [[CLLocationManager alloc] init]; locManager.delegate = self; locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度百米以?xún)?nèi) locManager.distanceFilter = 200;//水平或者垂直移動(dòng)200米調(diào)用代理更新位置 [locManager startUpdatingLocation];//啟動(dòng)位置更新
P.s. 定位要求的精度越高、屬性distanceFilter的值越小,應(yīng)用程序的耗電量就越大。
位置管理器有一個(gè)headingAvailable屬性,它指出設(shè)備是否裝備了磁性指南針。如果該屬性為YES,就可以使用Core Location來(lái)獲取航向(heading)信息。接收航向更新與接收位置更新極其相似,要開(kāi)始接收航向更新,可指定位置管理器委托,設(shè)置屬性headingFilter以指定要以什么樣的頻率(以航向變化的度數(shù)度量)接收更新,并對(duì)位置管理器調(diào)用方法startUpdatingHeading:
位置管理器委托協(xié)議定義了用于接收航向更新的方法。該協(xié)議有兩個(gè)與航向相關(guān)的方法:
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
return YES;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
}
第一個(gè)方法指定位置管理器是否向用戶(hù)顯示校準(zhǔn)提示。該提示將自動(dòng)旋轉(zhuǎn)設(shè)備360°。由于指南針總是自我校準(zhǔn),因此這種提示僅在指南針讀數(shù)劇烈波動(dòng)時(shí)才有幫助。當(dāng)設(shè)置為YES后,提示可能會(huì)分散用戶(hù)的注意力,或影響用戶(hù)的當(dāng)前操作。
第二個(gè)方法的參數(shù)newHeading是一個(gè)CLHeading對(duì)象。CLHeading通過(guò)一組屬性來(lái)提供航向讀數(shù):magneticHeading和trueHeading。這些值的單位為度,類(lèi)型為CLLocationDirection,即雙精度浮點(diǎn)數(shù)。這意味著:
如果航向?yàn)?.0,則前進(jìn)方向?yàn)楸保?/p>
如果航向?yàn)?0.0,則前進(jìn)方向?yàn)闁|;
如果航向?yàn)?80.0,則前進(jìn)方向?yàn)槟希?/p>
如果航向?yàn)?70.0,則前進(jìn)方向?yàn)槲鳌?/p>
CLHeading對(duì)象還包含屬性headingAccuracy(精度)、timestamp(讀數(shù)的測(cè)量時(shí)間)和description(這種描述更適合寫(xiě)入日志而不是顯示給用戶(hù))。下面演示了利用這個(gè)方法處理航向更新:
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
if(newHeading.headingAccuracy >=0)
{
NSString *headingDesc = [NSString stringWithFormat:@"%.0f degrees (true), %.0f degrees (magnetic)",newHeading.trueHeading,newHeading.magneticHeading];
NSLog(@"%@",headingDesc);
}
}
trueHeading和magneticHeading分別表示真實(shí)航向和磁性航向。如果位置服務(wù)被關(guān)閉了,GPS和wifi就只能獲取magneticHeading(磁場(chǎng)航向)。只有打開(kāi)位置服務(wù),才能獲取trueHeading(真實(shí)航向)。
下面的代碼演示了,當(dāng)存在一個(gè)確定了經(jīng)緯度的地點(diǎn),當(dāng)前位置離這個(gè)地點(diǎn)的距離及正確航向:
#import "ViewController.h"
#define kDestLongitude 113.12 //精度
#define kDestLatitude 22.23 //緯度
#define kRad2Deg 57.2957795 // 180/π
#define kDeg2Rad 0.0174532925 // π/180
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UILabel *lblMessage;
@property (strong, nonatomic) IBOutlet UIImageView *imgView;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *recentLocation;
-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
self.locationManager.distanceFilter = 1609; //1英里≈1609米
[self.locationManager startUpdatingLocation];
if([CLLocationManager headingAvailable])
{
self.locationManager.headingFilter = 10; //10°
[self.locationManager startUpdatingHeading];
}
}
/*
* According to Movable Type Scripts
* http://mathforum.org/library/drmath/view/55417.html
*
* Javascript:
*
* var y = Math.sin(dLon) * Math.cos(lat2);
* var x = Math.cos(lat1)*Math.sin(lat2) -
* Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
* var brng = Math.atan2(y, x).toDeg();
*/
-(double)headingToLocation:(CLLocationCoordinate2D)desired current:(CLLocationCoordinate2D)current
{
double lat1 = current.latitude*kDeg2Rad;
double lat2 = desired.latitude*kDeg2Rad;
double lon1 = current.longitude;
double lon2 = desired.longitude;
double dlon = (lon2-lon1)*kDeg2Rad;
double y = sin(dlon)*cos(lat2);
double x = cos(lat1)*sin(lat2) - sin(lat1)*cos(lat2)*cos(dlon);
double heading=atan2(y,x);
heading=heading*kRad2Deg;
heading=heading+360.0;
heading=fmod(heading,360.0);
return heading;
}
//處理航向
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
if(self.recentLocation!=nil && newHeading.headingAccuracy>=0)
{
CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
double course = [self headingToLocation:destLocation.coordinate current:self.recentLocation.coordinate];
double delta = newHeading.trueHeading - course;
if (abs(delta) <= 10)
{
self.imgView.image = [UIImage imageNamed:@"up_arrow.png"];
}
else
{
if (delta > 180)
{
self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
}
else if (delta > 0)
{
self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
}
else if (delta > -180)
{
self.imgView.image = [UIImage imageNamed:@"right_arrow.png"];
}
else
{
self.imgView.image = [UIImage imageNamed:@"left_arrow.png"];
}
}
self.imgView.hidden = NO;
}
else
{
self.imgView.hidden = YES;
}
}
//處理定位成功
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *curLocation = [locations lastObject];
if(curLocation.horizontalAccuracy >= 0)
{
self.recentLocation = curLocation;
CLLocation *destLocation = [[CLLocation alloc] initWithLatitude:kDestLatitude longitude:kDestLongitude];
CLLocationDistance distance = [destLocation distanceFromLocation:curLocation];
if(distance<500)
{
[self.locationManager stopUpdatingLocation];
[self.locationManager stopUpdatingHeading];
self.lblMessage.text = @"您已經(jīng)到達(dá)目的地!";
}
else
{
self.lblMessage.text = [NSString stringWithFormat:@"距離目的地還有%f米",distance];
}
}
}
//處理定位失敗
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if(error.code == kCLErrorLocationUnknown)
{
NSLog(@"Currently unable to retrieve location.");
}
else if(error.code == kCLErrorNetwork)
{
NSLog(@"Network used to retrieve location is unavailable.");
}
else if(error.code == kCLErrorDenied)
{
NSLog(@"Permission to retrieve location is denied.");
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS開(kāi)發(fā)系列--通知與消息機(jī)制詳解
這篇文章主要介紹了iOS開(kāi)發(fā)系列--通知與消息機(jī)制詳解,有需要的同學(xué)可以了解一下。2016-11-11
iOS實(shí)現(xiàn)類(lèi)似格瓦拉電影的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
這篇文章主要給大家介紹了利用iOS如何實(shí)現(xiàn)類(lèi)似格瓦拉電影的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),文中給出了詳細(xì)步驟實(shí)現(xiàn)代碼,對(duì)大家的學(xué)習(xí)和理解很有幫助,有需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2016-11-11
Objective-C 經(jīng)典字典數(shù)組排序 - 省市區(qū)
本文主要介紹Objective-C 字典數(shù)組排序,這里整理相關(guān)資料及實(shí)現(xiàn)示例代碼,有興趣的小伙伴可以參考下2016-09-09
IOS開(kāi)發(fā)代碼分享之獲取啟動(dòng)畫(huà)面圖片的string
本文是IOS開(kāi)發(fā)代碼分享系列的第一篇文章,這里分享下獲取啟動(dòng)畫(huà)面圖片的string的代碼,本代碼支持 iPhone 6 以下. 支持 iPhone 及 iPad,非常實(shí)用,希望對(duì)大家有所幫助2014-09-09
IOS實(shí)現(xiàn)左右兩個(gè)TableView聯(lián)動(dòng)效果
在我們?nèi)粘i_(kāi)發(fā)IOS中,經(jīng)常見(jiàn)到兩個(gè)tableview的聯(lián)動(dòng),滑動(dòng)一側(cè)tableview,另一側(cè)tableview跟著滑動(dòng),其實(shí)實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單,只是需要搞清楚他們之間的區(qū)別和聯(lián)系,下面一起來(lái)看看如何實(shí)現(xiàn)。2016-08-08
ios 使用xcode11 新建項(xiàng)目工程的步驟詳解
這篇文章主要介紹了ios 使用xcode11 新建項(xiàng)目工程 (值得注意的問(wèn)題),本文分步驟通過(guò)圖文的形式給大家展示,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Xcode中iOS應(yīng)用開(kāi)發(fā)的一般項(xiàng)目目錄結(jié)構(gòu)和流程簡(jiǎn)介
這篇文章主要介紹了Xcode中iOS應(yīng)用開(kāi)發(fā)的一般項(xiàng)目目錄結(jié)構(gòu)和流程簡(jiǎn)介,包括項(xiàng)目所需的一些平臺(tái)路徑如模擬器路徑等的介紹,需要的朋友可以參考下2016-02-02

