分享

cllocationmanager

 叹落花 2015-04-13
    有了iPhone,意味着我们不会再迷路了。有了iPhone内建的全球定位系统(Global Positioning System,GPS)硬件,以及一些创新的位置和地图软件,iPhone不仅随时知道你位于何处,而且还能显示给你看。

iPhone使用所谓的辅助GPS技术来搞清楚你位于何处。有了内建的GPS接收器,iPhone可以利用通信塔台和Wi-Fi热点之间的三角信息,来增加它发送给应用程序的位置数据的准确性。

在本章中,我们首先介绍Core Location,这是让我们快速且轻松地找到当前位置的框架。然后,我们将介绍Map Kit框架,它使我们能够把流行的Google Maps引擎所支持的地图添加到自己的应用中。我们还将看看如何轻松地执行反向地理编码来获取一个位置的地址,最后,我们把所有这些概念组合到一个地图应用程序中,它能够再现本地Maps应用程序的很多功能。

8.1   Core Location

Core Location是用来为iPhone应用添加位置识别功能的框架,其设计看上去很简单。CLLocationManager及其委托方法提供了一种机制,通过该机制来获取位置信息并发送给应用程序。直接创建一个实例,可选择性地设置一些精确度选项,然后调用startUpdatingLocation方法。

CLLocation事件通过CLLocationManager实例的委托方法产生。CLLocation对象不仅封装了地理信息,而且还有速度、高度、方向和精确度等信息。

让应用程序能够识别位置的方法如下:

1)创建一个基于视图的应用程序,将其保存为CoreLocationExample(如图8-1所示)。

2)在Groups & Files面板中,展开Targets部分,鼠标右键点击你的应用程序目标,并且选择Get Info。确保选中General标签,点击Linked Libraries列表底部的Add (+),并且添加CoreLocation框架(如图8-2所示)。

3)打开CoreLocationExampleViewController.h文件,导入CoreLocation框架,添加CL-LocationManagerDelegate协议声明,并创建一个实例变量来保存位置管理器(见程序清单8-1)。

图8-1   创建基于视图的应用程序               

图8-2   向项目中添加CoreLocation框架

4)切换到CoreLocationExampleView Controller.m文件,取消对viewDidLoad方法的注释,并添加如下代码:

lm = [[CLLocationManager alloc]

→init];

lm.delegate = self;

[lm startUpdatingLocation];

创建CLLocationManager实例,设置委托为视图控制器(self),然后告诉位置管理器开始发送位置事件。程序清单8-2给出了完整的应用程序代码。

程序清单8-1   Core Location应用程序的头文件

程序清单8-2   一个基本的Core Location应用程序

8.1.1   处理位置更新

不管你是否相信,就是这样,应用程序现在可以识别位置了。然而,我们还是没有任何办法来处理位置管理器给我们的信息。要做到这点,我们必须实现所需的委托方法(别忘了,我们还要把CLLocationManagerDelgate协议添加到CLLocationManager.h中)。

CLLocationManager只有两个委托方法。第一个是locationManager:didUpdateTo Location:fromLocation:,当位置管理器更新一个新的位置的时候调用。之前的位置和新的位置都会传递,除非这是第一个位置事件,在那种情况下fromLocation:将会是nil。

第二个委托方法locationManager:didFail WithError:,用于处理位置管理器试图获取一个位置值的时候所发生的任何错误。在这种情况下,可以通过一个stopUpdatingLocation调用来停止位置管理器,以节省电池电量。

程序清单8-3给出了更新后的Core LocationExampleViewController.m文件。

输出发送到控制台(如图8-3所示),给出了纬度/经度坐标、结果的精确度(在这个例子中精确到100米)、我们的速度、我们的路径或方向、位置事件何时发生的一个时间戳。

程序清单8-3   更新后的Core Location应用程序显示到控制台

图8-3   Debugger控制台显示位置信息

提示

·注意,要保持电池电量,只要有一个位置或发生错误,就在位置管理器上调用stopUpdatingLocation。

·当第一次启动位置管理器的时候(通过startUpdatingLocation),它通常会快速连续地产生许多位置更新。这通常只是校准位置或者缓存位置,往往不够精确并且可以忽略。我们将在本章稍后介绍一种技术来处理这种情况

8.1.2   在模拟器之外测试

从事过航海导航的读者可能注意到,当你自己运行前面的例子的时候,纬度和经度不能和你当前的位置对应(除非你现在Apple公司工作)。当基于Core Location的代码在iPhone模拟器中运行的时候,它总是报告一个位置:“1 Infinite Loop, Cupertino”(Apple公司总部所在地)。

显然路径和速度这样的元素在模拟器中不可能测试。此外,如果你拿着自己的iPhone走动并测试应用程序,显然不能在计算机屏幕上看到控制台的输出。

从现在开始,我们假设本章中的例子都是在一个实际的设备上而不是模拟器中运行。我们将向iPhone的主视图添加一个UITextView,并且编写一些代码在设备上而不是控制台上显示输出。

我们可以很容易地挂系UILabel控件或者其他更吸引人的UI元素,这是一种方便的快捷方式,让我们花费最少的力气就很容易地显示输出信息。

在iPhone屏幕上添加位置数据的记录:

1)在CoreLocationExampleViewController.h中,创建一个实例变量来保存你的视图(见程序清单8-4):

UITextView *logView;

2)切换到CoreLocationExampleView Controller.m,并且创建logToScreen:方法,我们将用此方法把文本显示到iPhone屏幕上:

CGPoint pt = logView.contentOffset;

我们首先设置一个变量来保存UIView的当前内容在其滚动视图中的位置:

logView.text = [NSString

→stringWithFormat:@"%@\n%@",

→logView.text,output];

接下来,附加新的文本,如果新文本比单个屏幕大的话,确保视图可以相应地滚动:

[logView setContentOffset:pt

→animated:NO];

[logView scrollRangeToVisible:

→NSMakeRange([logView.text length],

→0)];

程序清单8-4   更新后的头文件

图8-4   输出信息显示在iPhone上

3)更新viewDidLoad方法以便创建并添加新的视图:

logView = [[UITextView alloc]

→initWithFrame:[self.view bounds]];

logView.editable = NO;

logView.userInteractionEnabled =

→YES;

[self.view addSubview:logView];

4)最后,为了确保操作和NSLog()尽可能地接近,在该文件的顶部定义DCLog宏:

#define DCLog(format, ...)

→[self logToScreen:[NSString

→stringWithFormat:format,

→## __VA_ARGS__]];

现在,我们可以用一个DCLog()调用来替代对NSLog()的调用。输出信息将显示在iPhone的屏幕上(如图8-4所示)。

8.1.3   增加精确度

到目前为止,你可能已经注意到所接受的位置信息并不是很精确。这是因为位置管理器初次启动的时候常常返回缓存的或校准的“最佳猜测”值,而且精度会随着时间的增加而提高。

我们可以做一些事情来过滤掉这些不想要的位置事件,并且得到一个更加精确的结果。

我们到目前为止只使用了CLLocation事件的description属性,但是,还应该能够使用其他的几个属性:

·altitude—根据我们在海平面之上还是之下,它可能是一个正值或负值。

·coordinate—一个CLLocationCoordinate类型,包含了纬度和经度信息。

·course, speed—iPhone移动的方向和速度(以米为单位)。

·horizontalAccuracy , verticalAccuracy—坐标和海拔值的精确度

提示

·course以度来度量,北方是0,东方是90,南方是180,西方是270。你可能注意到了,在第一个示例中,course和speed值都是-1.0。由于只有单个的位置,意味着这些值都没有计算。

·horizontalAccuracy和verticalAccuracy的负值通常表示位置事件被忽略了。

8.1.4   添加超时

我们还考虑到节省iPhone电池的电量,设置超时,如果在一个合理的时间内没有接收到一个有效的位置,就关闭位置管理器。

为位置管理器添加超时:

1)在CoreLocationExampleViewController.h中,添加一个新的实例变量来保存超时:

NSTimer *timer;

2)切换到CoreLocationExampleView Controller.m,并且向viewDidLoad方法中添加如下新的代码:

lm.desiredAccuracy =

→kCLLocationAccuracyBest;

lm.distanceFilter =

→kCLDistanceFilterNone;

这告诉位置管理器,它应该有多么精确,以及用户必须移动多远(以米为单位)后才会产生一个新的位置事件。

然后,我们把定时器设置为1分钟后过时:

timer = [NSTimer scheduledTimer

→WithTimeInterval:60 target:self

→selector:@selector(locationManager

→DidTimeout:userInfo:) userInfo:nil

→repeats:false];

desiredAccuracy代码告诉位置管理器在产生位置事件的时候,它应该努力达到什么样的精度。这个属性可能的值范围从“best”到3千米。然而,这还是值得注意的,尽管位置管理器将试图实现定义的精确度,但这并不能确保。

在这个例子中,我们对desiredAccuracy和distanceFilter都使用最精确的设置。使用这些设置可能会花费较长的时间来返回一个位置,由此会对iPhone的电池寿命有更多的影响,记住这一点很重要。

3)更新位置管理器委托,用于检查位置事件的时间:

NSTimeInterval eventAge =

→[newLocation.timestamp

→timeIntervalSinceNow];

if (abs(eventAge) < 5.0)

我们只对过去5秒钟内发生的事件感兴趣。这将会过滤掉任何缓存的位置数据。如果一个事件足够新,那么我们接下来检查精确度:

if ([newLocation horizontalAccuracy]

→> 0.0f && [newLocation

→horizontalAccuracy] <= 100.0f)

这将会只获取那些精确到你的实际位置的100米以内的事件(也确保忽略了负值这样的无效值)。

最后,停止位置管理器和定时器,并且输出结果。

4)还有两件事情要做。首先,需要处理无法找到一个位置的情况:

(void)locationManagerDidTimeout:

→(NSTimer *)aTimer userInfo:(id)

→userInfo

这需要直接停止位置管理器,并且在屏幕上显示一个错误。最后,如果发生错误的话,确保停止定时器,可通过把如下代码加入到locationManager:didFailWithError:委托方法来做到:

[timer invalidate];

程序清单8-5   完整的Core Location代码

图8-5显示了一次成功位置搜索的结果。尝试在你的iPhone上运行这些代码,当位置事件发生的时候,你将会看到它们(如果你在家中的话,可能需要增加超时值),而且精确度会随着时间而增加。尝试修改desiredAccuracy和distanceFilter的值,看看它们如何影响到改变或删除缓存位置检查的结果。

提示

·由于位置管理器是逐渐增加精确度的,并且在一个位置上“校准为零”,这实际上可能是第一个结果,尽管报告为不精确的位置,但实际上是正确的位置。

·快速地显示潜在的不精确的信息与让用户为更精确的结果等待更长时间,这两者之间总是有一种平衡。前面示例中的代码只是一种方法。另一种思路可能是减少想要的精度级别,并且采用统计地址事件的策略,等达到一定数目的时候接受一个作为有效的地址。可以根据应用程序的需求来确定选取哪种方法。

图8-5   位置搜索的结果

8.1.5   访问指南针

如果iPhone包含一个指南针,我们可以用与获取位置更新同样的方式来获取方向信息。位置管理器把方向更新生成为CLHeading对象,其中包含了表示方向值的属性,该方向值对应于磁场和正北方。这些属性都以度来表示,0表示北方,180表示南方。

检查iPhone是否支持方向更新,我们可以查看位置管理器的headingAvailable属性。开始和停止监听方向事件,我们分别调用startUpdatingHeading和stopUpdatingHeading方法。就像位置更新一样,方向更新也通过委托方法来管理:

·locationManager:didUpdateHeading:—当接收到一个和之前方向不同的新方向(且比headingFilter属性中指定的值大)的时候调用。

·locationManagerShouldDisplayHeading Calibration:manager—如果iPhone需要校准,调用该方法。通常当一个新iPhone的指南针还没有使用过的时候,会发生这种情况。返回YES将显示指南针校准面板。我们可以通过调用dismissHeadingCalibrationDisplay来关闭该面板,或者可以通过向这个方法返回NO来阻止该面板显示。

到目前为止的例子中,我们主要集中于在停止位置管理器之前尽可能精确地获取单个位置。你可能想要有一个真正的应用程序,能够随着你的走动或开车,连续地更新位置、显示路径、速度和方向信息。我们将在本章8.3节“综合应用”中看到这样的例子。          

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多