永远不会调用CLLocationManager的didExitRegion

时间:2012-12-27 23:05:35

标签: ios cocoa-touch cllocationmanager geofencing

我试图设置一个小型地理围栏(100米),以便在用户离开家时提醒用户。为此,我请求用户当前位置如下:

- (void)requestUsersCurrentLocation
{
    if (self.locationManager == nil) self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    self.locationManager.distanceFilter = 100;
    [self.locationManager startUpdatingLocation];
}

使用CLLocationManager的委托方法我检查应用程序是否能够确定用户的当前位置。在我的测试中,这可以正常工作,应用程序继续调用我的attemptToRegisterGeofenceAlertForLocation:方法

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *location = [locations lastObject];
    NSDate *eventDate = location.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
    if (abs(howRecent) < 15.0)
    {
        [self.locationManager stopUpdatingLocation];

        if([self attemptToRegisterGeofenceAlertForLocation:location])
        {
            [[NSNotificationCenter defaultCenter] postNotificationName:kGeofenceSetupSuccess object:nil];
        }
        else
        {
            [[NSNotificationCenter defaultCenter] postNotificationName:kGeofenceSetupFailure object:nil];
        }
    }
}

到目前为止一切顺利。这是我的自定义功能,用于在用户当前位置周围注册相对较小的地理围栏:

- (BOOL)attemptToRegisterGeofenceAlertForLocation:(CLLocation *)location
{
    // Do not create regions if support is unavailable or disabled
    if (![CLLocationManager regionMonitoringAvailable]) return NO;

    // Check the authorization status
    if (([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized) && ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined)) return NO;

    // Clear out any old regions to prevent buildup.
    if ([self.locationManager.monitoredRegions count] > 0)
    {
        for (id obj in self.locationManager.monitoredRegions)
        {
            [self.locationManager stopMonitoringForRegion:obj];
        }
    }

    // If the overlay's radius is too large, registration fails automatically,
    // so clamp the radius to the max value.
    CLLocationDegrees radius = 100; // meters?
    if (radius > self.locationManager.maximumRegionMonitoringDistance)
    {
        radius = self.locationManager.maximumRegionMonitoringDistance;
    }

    // Create the region to be monitored.
    CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:location.coordinate radius:radius identifier:kGeofenceIdentifier];
    [self.locationManager startMonitoringForRegion:region];

    return YES;
}

当用户退出地理围栏区域时,我会这样回复:

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    if([region.identifier isEqual:kGeofenceIdentifier])
    {
        if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
        {
            // Fire a UILocationNotification
        }
        else
        {
            // Fire a UIAlertView
        }
    }
}

我已确定应用程序能够获取用户位置并且地理围栏已正确注册,但我无法在实际设备(启用3G的iPhone)上触发它。我离开了地理围栏区域并且没有收到任何通知就走了几英里。我通过彻底改变我的位置,成功地设法在模拟器中收到警报。

我做错了什么?

3 个答案:

答案 0 :(得分:1)

看起来您的半径设置为100M(好)。距离过滤器也设置为100M,可能只是对你不利。在模拟器中,您可以获得非常精确的更新,因为您必须将它们传递进去。在实际测试中,您获得的Wifi近似和细胞三角测量的坐标要小于精确坐标。

我的经验是,如果我使用kCLLocationAccuracyBest进行距离过滤,我会得到非常好的结果。由于区域监控已经从系统供电不足,因此只需将滤波器设置为最佳状态即可获得电池电量。

答案 1 :(得分:1)

事实证明,此问题的解决方案是确保在主线程上初始化CLLocationManager。根据Apple的文档:

  

您的位置管理器对象的配置必须始终在a上进行   具有活动运行循环的线程,例如应用程序的主循环   线程。

答案 2 :(得分:0)

你已经得到了答案,但有时问题可能是因为没有出去太多而且没有长时间呆在外面或者有时候你已经这样做了,但是因为你所在的地区是喜欢设置一个非常小的东西,如5米(你永远不应该做的事情),你移动了100米

  

在iOS模拟器中测试您的区域监控代码时   设备,意识到区域事件可能不会立即发生   区域边界交叉。为了防止虚假通知,iOS   不提供区域通知直到某个阈值   条件得到满足。具体来说,用户的位置必须越过   区域边界,离开边界最小距离,   和保持在最小距离至少20秒之前   报告通知。

     

特定阈值距离由硬件确定   目前可用的定位技术。例如,   如果 Wi-Fi被禁用,则区域监控显着减少   准确。但是,出于测试目的,您可以假设   最小距离约为200米

     

来自Apple documentation: Region Monitoring