Core Location 是 iOS SDK 中一个提供设备位置的框架。可以使用三种技术来获取位置:GPS、蜂窝或 WiFi。在这些技术中,GPS 最为精准,如果有 GPS 硬件,Core Location 将优先使用它。如果设备没有 GPS 硬件 (如 WiFi iPad) 或使用 GPS 获取当前位置时失败,Core Location 将退而求其次,选择使用蜂窝或 WiFi。
Core Location 的大多数功能是由位置管理器 (CLLocationManager) 提供的,可以使用位置管理器来指定位置更新的频率和精度,以及开始和停止接收这些更新。
要使用位置管理器,必须首先将框架 Core Location 加入到项目中,再导入其接口文件:
接下来,需要分配并初始化一个位置管理器实例、指定将接收位置更新的委托并启动更新:
- #import < CoreLocation / CoreLocation.h >
位置管理器委托 (CLLocationManagerDelegate) 有两个与位置相关的方法:
- CLLocationManager * locManager = [[CLLocationManager alloc] init];
- locManager.delegate = self; [locManager startUpdatingLocation];
- //[locManager stopUpdatingLocation];
- - (void) locationManager: (CLLocationManager * ) manager didUpdateLocations: (NSArray * ) locations {
- } - (void) locationManager: (CLLocationManager * ) manager didFailWithError: (NSError * ) error {
- }
第一个方法处理定位成功,manager 参数表示位置管理器实例;locations 为一个数组,是位置变化的集合,它按照时间变化的顺序存放。如果想获得设备的当前位置,只需要访问数组的最后一个元素即可。集合中每个对象类型是 CLLocation,它包含以下属性:
coordinate — 坐标。一个封装了经度和纬度的结构体。
altitude — 海拔高度。正数表示在海平面之上,而负数表示在海平面之下。
horizontalAccuracy — 位置的精度 (半径)。位置精度通过一个圆表示,实际位置可能位于这个圆内的任何地方。这个圆是由 coordinate(坐标) 和 horizontalAccuracy(半径)共同决定的,horizontalAccuracy 的值越大,那么定义的圆就越大,因此位置精度就越低。如果 horizontalAccuracy 的值为负,则表明 coordinate 的值无效。
verticalAccuracy — 海拔高度的精度。为正值表示海拔高度的误差为对应的米数;为负表示 altitude(海拔高度) 的值无效。
speed — 速度。该属性是通过比较当前位置和前一个位置,并比较它们之间的时间差异和距离计算得到的。鉴于 Core Location 更新的频率,speed 属性的值不是非常精确,除非移动速度变化很小
- - (void) locationManager: (CLLocationManager * ) manager didUpdateLocations: (NSArray * ) locations {
- CLLocation * curLocation = [locations lastObject];
- if (curLocation.horizontalAccuracy > 0) {
- NSLog(@"当前位置:%.0f,%.0f +/- %.0f meters", curLocation.coordinate.longitude, curLocation.coordinate.latitude, curLocation.horizontalAccuracy);
- }
- if (curLocation.verticalAccuracy > 0) {
- NSLog(@"当前海拔高度:%.0f +/- %.0f meters", curLocation.altitude, curLocation.verticalAccuracy);
- }
- }
应用程序开始跟踪用户的位置时,将在屏幕上显示一个是否允许定位的提示框。如果用户禁用定位服务,iOS 不会禁止应用程序运行,但位置管理器将生成错误。
第二个方法处理这种定位失败,该方法的参数指出了失败的原因。如果用户禁止应用程序定位,error 参数将为 kCLErrorDenied;如果 Core Location 经过努力后无法确认位置,error 参数将为 kCLErrorLocationUnknown;如果没有可供获取位置的源,error 参数将为 kCLErrorNetwork。
通常,Core Location 将在发生错误后继续尝试确定位置,但如果是用户禁止定位,它就不会这样做;在这种情况下,应使用方法 stopUpdatingLocation 停止位置管理器。
可根据实际情况来指定位置精度。例如,对于只需确定用户在哪个国家的应用程序,没有必要要求 Core Location 的精度为 10 米。要指定精度,可在启动位置更新前设置位置管理器的 desiredAccuracy。有 6 个表示不同精度的枚举值
- - (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."); [manager stopUpdatingLocation];
- }
- }
对位置管理器启动更新后,更新将不断传递给位置管理器委托,直到停止更新。您无法直接控制这些更新的频率,但可使用位置管理器的属性 distanceFilter 进行间接控制。在启动更新前设置属性 distanceFilter,它指定设备 (水平或垂直) 移动多少米后才将另一个更新发送给委托。下面的代码使用适合跟踪长途跋涉者的设置启动位置管理器:
- extern const CLLocationAccuracy kCLLocationAccuracyBestForNavigation;
- extern const CLLocationAccuracy kCLLocationAccuracyBest;
- extern const CLLocationAccuracy kCLLocationAccuracyNearestTenMeters;
- extern const CLLocationAccuracy kCLLocationAccuracyHundredMeters;
- extern const CLLocationAccuracy kCLLocationAccuracyKilometer;
- extern const CLLocationAccuracy kCLLocationAccuracyThreeKilometers;
- CLLocationManager * locManager = [[CLLocationManager alloc] init];
- locManager.delegate = self;
- locManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
- locManager.distanceFilter = 200; [locManager startUpdatingLocation];
P.s. 定位要求的精度越高、属性 distanceFilter 的值越小,应用程序的耗电量就越大。
位置管理器有一个 headingAvailable 属性,它指出设备是否装备了磁性指南针。如果该属性为 YES,就可以使用 Core Location 来获取航向 (heading) 信息。接收航向更新与接收位置更新极其相似,要开始接收航向更新,可指定位置管理器委托,设置属性 headingFilter 以指定要以什么样的频率 (以航向变化的度数度量) 接收更新,并对位置管理器调用方法 startUpdatingHeading:
位置管理器委托协议定义了用于接收航向更新的方法。该协议有两个与航向相关的方法:
- - (BOOL) locationManagerShouldDisplayHeadingCalibration: (CLLocationManager * ) manager {
- return YES;
- } - (void) locationManager: (CLLocationManager * ) manager didUpdateHeading: (CLHeading * ) newHeading {
- }
第一个方法指定位置管理器是否向用户显示校准提示。该提示将自动旋转设备 360°。由于指南针总是自我校准,因此这种提示仅在指南针读数剧烈波动时才有帮助。当设置为 YES 后,提示可能会分散用户的注意力,或影响用户的当前操作。
第二个方法的参数 newHeading 是一个 CLHeading 对象。CLHeading 通过一组属性来提供航向读数:magneticHeading 和 trueHeading。这些值的单位为度,类型为 CLLocationDirection,即双精度浮点数。这意味着:
如果航向为 0.0,则前进方向为北;
如果航向为 90.0,则前进方向为东;
如果航向为 180.0,则前进方向为南;
如果航向为 270.0,则前进方向为西。
CLHeading 对象还包含属性 headingAccuracy(精度)、timestamp(读数的测量时间) 和 description(这种描述更适合写入日志而不是显示给用户)。下面演示了利用这个方法处理航向更新:
- - (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 分别表示真实航向和磁性航向。如果位置服务被关闭了,GPS 和 wifi 就只能获取 magneticHeading(磁场航向)。只有打开位置服务,才能获取 trueHeading(真实航向)。
下面的代码演示了,当存在一个确定了经纬度的地点,当前位置离这个地点的距离及正确航向:
- #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 = @"您已经到达目的地!";
- } 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
来源: http://lib.csdn.net/article/ios/42143