第十四章 我在哪里?使用Core Location定位功能
是最精确的,但在第一代iPhone上不可用。蜂窝基站三角网定位在基本密度较高的区域非常精确。最后一个选项WPS使用iPhone的
Wi-Fi连接的IP地址,通过参考已知服务提供商及其服务区域的大型数据库来猜测你的位置。WPS是不精确的,并且有时会有数英里
的误差。
所有这3种方法都很耗电,因此在使用Core Location时请记住这一点。除非绝对必要,否则不应该对你的位置进行多次轮询。使用
Core Location时,可以根据需要指定精度。通过仔细指定所需的绝对最低精度级别,可以防止不必要的电池消耗。
Core Location所依赖的技术隐藏在你的应用程序中,我们没有告知Core Location是使用GPS、三角网还是WPS。我们只是告知他我
们希望的精度级别,他将决定使用哪种技术。
14.1 位置管理器
Core Location API实际上非常易于使用。我们将使用的主类是CLLocationManager,通常称为Location Manager。
CLLocationManager *locationManager=[[CLLocationManager alloc] init];
创建了实例之后,并没有开始轮询我们的位置。我们创建一个委托,并将其分配给位置管理器。当位置信息可用时,位置管理器会
调用我们的委托方法。这可能会花费一些时间,甚至需要几秒钟。我们的委托必须符合CLLocationManagerDelegate协议。
14.1.1 设置所需的精度
设置委托之后,你还要设置精度,记住,精度级别越高,消耗电量就会越多,另外,不能保证你会获得所需的精度。
locationManager.delegate=self;
locationManager.desireAccuracy=kCLLocationAccuracyBest;
精度是使用CLLocationAccuracy值进行设置的,类型定义为double。该值的单位为米(m),因此如果你指定desireAccuracy为10,
则是告知尝试确定当前位置的10m范围之内的区域。kCLLocationAccuracyBest会通知Core Location使用当前可用的最高精度的方法
。还可以使用kCLLocationAccuracyNearestTenMeters、kCLLocationAccuracyHunderedMeters、kCLLocationAccuracyKilometer和
kCLLocationAccuracyThreeKilometers。
14.1.2 设置距离筛选器
默认情况下,位置管理器将通知委托任何检测到的在位置方面的更改。通过指定距离筛选器,告知位置管理器不要将每个更改都通
知你,仅当位置更改超过特定数量时 才通知你。设置距离筛选器可以减少应用程序所执行的轮询数量。距离筛选器也是以米为单位
的。指定距离筛选器为1000,是告知位置管理器知道iPhone已经从其以前报告的位置移动至少1000m之后才通知委托。
locationManager.distanceFilter=1000.0f;
locationManager.distanceFilter=kCLDistanceFilterNone;//返回默认设置
14.1.3 启动位置管理器
当你准备好开始轮询位置时,通知位置管理器启动,然后他将离开并做自己的事情,然后在他已经确定当前位置时调用委托方法。
在你告知他停止之前,只要他感知到任何超过当前距离筛选器的更改,他就会继续调用你的委托方法。
[locationManager startUpdatingLocation];
14.1.4 更明智地使用位置管理器
如果只需要确定当前位置而不需要连续轮询位置,则当他获取应用程序所需的信息之后,你应该让位置委托停止位置管理器。如果
需要连续轮询,则确保只要可能就停止轮询。请记住,只要你从位置管理器获得更新,就会消耗电池。若要告知位置管理器停止工
作,请调用:
[locationManager stopUpdatingLocation];
14.2 位置管理器委托
CLLocationManagerDelegate协议,该协议有两种方法。这两种方法都是可选的。当位置管理器已经确定当前位置或当他就爱南侧到
位置的更改时将调用其中一个方法。当位置管理器遇到错误时将调用另一个方法。
14.2.1 获取位置更新
当位置管理器希望通知其委托当前位置时,他将调用
locationManager:didUpdateToLocation:fromLocation:方法。第二个参数是一个CLLocation对象。第一次调用该方法时,以前的位
置对象将为nil。
14.2.2 使用CLLocation获取经纬度
CLLocation具有5个属性,纬度和经度存储在一个名为coordinate的属性中。若要以度为单位获取纬度和经度,请使用以下代码:
CLLocationDegrees latitude=theLocation.coordinate.latitude;
CLLocationDegrees longitude=theLocation.coordinate.longitude;
CLLocation对象还可以告诉你位置管理器在其纬度和经度计算方面的精确程度。horizontalAccuracy属性描述以coordinate作为其
中心的一个圆的半径。horizontalAccuracy的值越大,Core Location所确定的位置就越不确定。
你可以看到horizontalAccuracy在Maps应用程序中的图形表示。当horizontalAccuracy为负值时,表示由于某些原因,你不能依赖
coordinate的值。
CLLocation对象还具有一个altitude的属性,可以告诉你在海平面以上或以下多少米:
CLLocationDistance altitude=theLocation.altitude;
每个CLLocation对象都有一个名为verticalAccuracy的属性,该属性表示Core Location在其海拔方面的精确程度。海拔的值可能与
verticalAccuracy的值相差很多米,并且verticalAccuracy为负值,同样表示altitude为无效值。
除了这些属性之外,CLLocation还有一个非常有用的实例方法,即两个CLLocation之间的距离。
CLLocationDistance distance=[fromLocation getDistanceFrom:toLocation];
返回的距离将是大圆计算的结果,该计算忽略了海拔属性,并且假设这两个点处于同一海平面来计算该距离。对于大多数场合来说
,大圆计算已经足够了,但是如果在计算距离时考虑海拔,你需要自己实现。
14.2.3 错误通知
如果Core Location无法确定你的位置,他会调用
locationManager:didFailWithError:的代理方法
最可能的错误原因是用户拒绝访问。Location Manager的使用必须由用户进行授权,因此应用程序第一次确定位置时,会在屏幕上
弹出一个警告,询问用户是否确定让当前应用程序访问你的位置。
如果用户单击Don't Allow,则Location Manager会使用包含错误代码的kCLErrorDenied的locationManager:didFailWithError:通
知委托。编写此项目时,Location Manager唯一支持的其他错误代码为kCLErrorLocationUnknown,他表示Core Location无法确定
位置,但他将不断尝试。kCLErrorDenied错误通常表示,在当前会话的其余时间,应用程序都将无法访问Core Location。另一方面
,kCLErrorLocationUnknown错误表示问题可能是临时的。
说明:在仿真器中,不会提示你对Core Location的访问,并且将使用一个超级机密的算法来确定位置。
14.3 尝试使用Core Location
让我们创建一个小型应用程序来检测iPhone的当前位置以及该程序运行期间所移动的总路程。
创建一个基于视图的新项目 WhereAmI。修改WhereAmIViewController.h:
#import <CoreLocation/CoreLocation.h>
@interface WhereAmIViewController:UIViewController <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
CLLocation *startingPoint;
IBOutlet UILabel *latitudeLabel;
IBOutlet UILabel *longitudeLabel;
IBOutlet UILabel *horizontalAccuracyLabel;
IBOutlet UILabel *altitudeLabel;
IBOutlet UILabel *verticalAccuracyLabel;
IBOutlet UILabel *distanceTravelLabel;
}
..
@end
注意
#import <CoreLocation/CoreLocation.h>
然后设置WhereAmIViewController.xib。
然后修改WhereAmIViewController.m:
- (void)viewDidLoad {
self.locationManager=[[CLLocationManager alloc] init];
locationManager.delegate=self;
locationManager.desireAccuracy=kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if (startingPoint==nil)
self.startingPoint=newLocation;
NSString *latitudeString=[[NSString alloc] initWithFormat:@"%g°",newLocation.coordinate.latitude];
latitudeLabel.text=latitudeString;
[latitudeString release];
...
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSString *errorType=(error.code==kCLErrorDenied)?@"Access Denied":@"Unknown error";
UIAlertView *alert=...
}
@end
14.3.1 更新位置管理器
14.3.2 确定移动距离
在编译程序之前,你需要向项目中添加CoreLocation.framework。