(iOS)通过locationManager的多个通知:didExitRegion:退出某个区域时

时间:2012-02-16 06:41:05

标签: iphone ios cocoa-touch core-location

我正在开发一个基于位置的应用程序,它利用了CLLocationManager区域监控。

我正在使用单个CLLocationManager和单个委托(在启动时在主应用程序委托中设置),我注意到我经常会多次调用我的委托(在locationManager上:didExitRegion:退出受监控区域时 - 通常是两次通话,但有时更多。有没有其他人经历过这个,或者有任何想法会出现什么问题?

我在app delegate中实例化的类中实例化CLLocationManager,如下所示:

    _locationManager = [[CLLocationManager alloc] init];
    _locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    _locationManager.delegate = self;

我正在设置像这样的区域监控:

    // The region instance has a radius of 300 meters
    [_locationManager startMonitoringForRegion:region desiredAccuracy:1000];

据我所知documentation,提供所需的准确度1000意味着只有在我们距离该地区1000米的地方才能调用locationManager:didExitRegion:

另外一点 - 就我所见,如果我在车里(因此旅行很快),我只会收到多次通知。如果我骑自行车或步行,似乎不会发生这种情况。关于我做错了什么(或者如果这是其他人已经遇到过的问题)的任何指示都表示赞赏。

5 个答案:

答案 0 :(得分:6)

tl;博士:这真的很简单 - 你得到的信息与苹果公司可以提供的信息一样多,因为你正在穿越大厦的边界 - 这意味着,根本不是真正好的数据。

现在真实的故事:

通常,CoreLocation只能通过几种方式确定一个人的位置:

  • 通过GPS。这是非常准确的(几十米),但需要很大的力量才能使用。
  • 通过WiFi。这通常一般准确,虽然wifi基站可以并且确实发生变化,这使其模糊。这种方法交叉引用该区域的wifi站点与一些已知的准确位置 - 因此它知道它何时看到它与特定区域相关联的“FooWifiStation”,通过精确仪器测量,或者甚至是其他打开GPS的手机(其中是有争议的,我们可能永远不知道Apple是否使用这种方法)
  • 靠细胞塔位置。这些不会移动,所以当你与塔相关联时,它知道你在一个大的模糊点覆盖范围内。这是最不准确但功耗最低的,因为您的手机已经正在进行工作以保持联系。

如果你进入地图应用程序“冷”,你甚至可以看到这个:你立即开始使用一个大的模糊蓝点(至少1km我在哪里),然后它收缩,因为它得到一个wifi位置修复,当GPS得到修复时,它或多或少会消失。它正在做[cell tower] => [wifi] => [gps]实时准确度。

根据我的经验,“重要的地点变更”条款意味着“只要我们不需要做更多的工作或花费更多,我们会在你大大移动的时候通知你,能够为你提供数据。这意味着事实上你可以依靠的最好的就是在手机信号塔之间使用过渡。苹果故意留下这个模糊的,因为如果另一个应用程序使用的东西具有更好的分辨率 - 比如说,你打开Maps.app,它会得到一个GPS修复 - 你可能会突然在位置上得到一个伟大的修复,但你不能完全依赖于那种情况。你已经问过对于位置的“弱参考”。

现在想想当你在车里徘徊时会发生什么:你的手机必须管理这种转变。它会被交付,同时与多个塔楼交谈,这样的事情,在你进行对话时管理一个必须可行的无缝过渡。对于任何小盒子来说,这都是一个非常巧妙的壮举。必要的是,这会让你在位置更新中看到一些反弹,因为,在手机上,当你协商转换时,你正在手机信号塔之间振动。

因此,半径的真正意义在于您对大致相同的数据感兴趣 - 但API 不会保证您在该半径范围内获得更新。你可以把它想象成苹果公司说的,“我们会把你的准确性分成三组之一 - 但我们没有给你一个列举,因为我们希望保留开发更好的方法来修复你的位置的权利你必须改变你的代码“。当然,实际上,如果他们改变了获取位置的方法,任何真正的应用程序都会改变,因为他们对这个位置的内容非常模糊。

换句话说,您将在的单元格塔处获得一个位置修复,并对该准确度的准确程度进行了一些完全猜测;当你移动到下一个塔时,你会立即跳到它的位置,同样模糊的修复 - 这有意义吗? CoreLocation告诉你,你是的蜂窝塔,其精确度远远超过了蜂窝塔的信号;当你移动到另一个塔时,你可能会获得切换的弹性。

因此,当谈到它时,要真正做好工作,你必须假设数据是“好”,“好”或“坏”之一,并使用其他方法 - 如Kalmann filters - 如果你真的需要更好地猜测用户在哪里。作为一个零阶近似,我只是根据更新的 time 来消除回调,并假设用户并没有在几秒钟内来回跳跃数公里,而是沿着第一次新更新的方向前进。

答案 1 :(得分:5)

我认为你会更好,使用比半径300米更小的所需精度,即kCLLocationAccuracyHundredMeters。你试过吗? 没有关于基础逻辑的文档,但是我认为,desiredAccuracy可以被视为 minimium 距离,你必须去旅行,因为该运动被视为“过境点” ”。
区域监测基于“重要位置事件”,而不是GPS - 否则电池将不会持续半天。

如果您使用如此高的desiredAccuracy,系统可能会获得多个重要的位置事件(这些事件似乎每隔500米生成一次 - 也取决于您在该区域拥有多少个无线网络。 在这种情况下,系统可能会将因重大变化而产生的当前位置与您所在地区所有区域的距离进行比较。 如果您距离您所在地区的对面1000米以外,它可能会再次通知,并且只有在您所在地区的双方以外的1000米外停止通知。

精确度参数的原因是避免过境,如果你太靠近边界,那么你在边境外旅行时就不会看到内外 - 外 - 等等...... p>

在我的应用程序中,我尝试了许多不同的半径和精度组合,并且在500米/ 100米和100米/ 10米的位置上进行了测试 - 这些在我的实际工作中非常有效。在市内的szenario中。

(另见优秀文章系列http://longweekendmobile.com/2010/07/22/iphone-background-gps-accurate-to-500-meters-not-enough-for-foot-traffic/

答案 2 :(得分:2)

我敢打赌这是你的问题,locationManager:didExitRegion:会在每个区域都调用iPhone上的每个应用程序中的每个位置管理员。您需要将作为参数发送的区域标识符字符串与您希望应用程序当前执行某些操作的区域的区域标识符字符串进行比较。

当您发送[_locationManager startMonitoringForRegion:region desiredAccuracy:1000];时,您正在创建一个区域,如果它尚不存在则无法监控。这与向NSNotification Center添加观察者不同。 Apple的区域对象盲目地将通知发送给每个CLLocationManager,然后CLLocationManager将消息发送给代表。

答案 3 :(得分:0)

首先,文档并未指出在生成didExitRegion事件之前,单独的desiredAccuracy确定了您需要离开区域的距离。如果你想微调事件被触发的频率,你也需要使用distanceFilter,它决定了在事件被触发之前需要移动的水平移动。

如果使用distanceFilter不起作用,那么我建议如下:

  1. 如果您使用的是NSNotificationCenter,请确保已通过[[NSNotificationCenter defaultCenter] removeObserver:self]从通知中删除了其他类。您可以选择为此调用指定名称和对象。

  2. 1000公里是一个大半径,很有可能与附近的许多地区相交。我会为此参数尝试较小的数字,以查看是否不会减少您收到的退出通知的数量。唯一表明这可能不是解决方案的是你没有说你也收到了didEnterRegion的爆炸声。

  3. 最后,我会检查在didExitRegionEvent中传递的CLRegion的标识符,看看你是否不能自己设置区域的NSDictionary。您必须在didEnterRegion上为字典设置一个区域,并在didExitRegion上将其删除。因此,在didExitRegion上,您所要做的就是通过检查您是否已经拥有该区域来确保您对该地区感兴趣。我猜想CLRegion已经配备了isEqual:和hash以允许它存储在一个集合中。

  4. 祝你好运!

答案 4 :(得分:0)

我发现为了省电,你必须使用monitorForSignificantLocationChange。 我的解决方案是过滤掉同一地区60秒内的多个警报:

-(BOOL)checkForMultipleNotifications:(GeoFenceModel*)fence
{
  GeoFenceModel *tempFence = [fenceAlertsTimeStamps objectForKey:fence.geoFenceId];
  if(tempFence == nil){
    fence.lastAlertDate = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
    [fenceAlertsTimeStamps setObject:fence forKey:fence.geoFenceId];
    NSLog(@"checkForMultipleNotifications : no Notifications found for Region : %@, Adding to Dictionary with timestamp %.1f",fence.geoFenceId,fence.lastAlertDate.doubleValue);
  }
  else if(([[NSDate date] timeIntervalSince1970] - fence.lastAlertDate.doubleValue) <= 60.0){
    NSLog(@"checkForMultipleNotifications : Multiple region break notifications within 60 seconds, skipping this alert");
    return NO;
  }
  return YES;
}