iOS地理围栏,当监测开始时如何处理在区域内的情况?

10

我一直不知道如何处理这种情况:当调用startMonitoringForRegion时,手机已经在一个区域内。其他问题建议在didStartMonitoringForRegion中调用requestStateForRegion,然后调用didDetermineState: forRegion:方法。因此,代码看起来像这样:

- (void)viewDidLoad {
    //location manager set up etc...
    for (Object *object in allObjects){

        CLRegion *region = [self geofenceRegion:object];
        [locationManager startMonitoringForRegion:region];
     }
}

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {

    [self.locationManager requestStateForRegion:region];
    [self.locationManager performSelector:@selector(requestStateForRegion:) withObject:region afterDelay:5];
 }

- (void)locationManager:(CLLocationManager *)manager
  didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {

    if (state == CLRegionStateInside){
        [self locationManager:locationManager didEnterRegion:region];
    }  
}

显然,方法geofenceRegion是我自己写的,并且它工作正常,而对象包含的内容如纬度、经度和半径也都可以正常使用,因此这不是问题所在。

无论如何,上面代码的问题在于,如果用户在将区域添加到他们的设备时已经在该区域内(即完成了didEnterRegion),则它确实可以工作。 然而,问题在于每当通过苹果文档的边界区域之一时,也会调用didDetermineState: forRegion:方法:

每当区域发生边界转换时,位置管理器都会调用此方法。 它除了调用locationManager:didEnterRegion:和locationManager:didExitRegion:方法之外还调用此方法。 位置管理器还响应对其requestStateForRegion:方法的调用并调用此方法。

由于这个原因,每当进入一个区域时,didEnterRegion会自动被调用,但随后didDetermineState: forRegion:也会被自动调用,导致再次调用didEnterRegion,结果导致区域被重复进入了两次,而我只想要它进入一次。怎样才能避免这种情况呢?

谢谢你的帮助。

解决方案

解决方法非常简单,只是我走了弯路。我必须选择使用两个方法didEnterRegion:didExitRegion或者使用didDetermineState: forRegion并创建自己的进入和退出区域的方法,两者都不应该使用

所以我选择只使用didDetermineState: forRegion方法,我的代码现在看起来像这样:

请注意,使用这种方法,如果不在区域内,则会为该区域调用退出区域的方法。 如果像我一样,您只希望在进入区域后才发生退出,您将需要某种检查区域是否已经进入的方法(我自己使用core data,因为我已经在使用它来存储区域的其他方面)。

- (void)viewDidLoad {
    //location manager set up etc...
    for (Object *object in allObjects){

        CLRegion *region = [self geofenceRegion:object];
        [locationManager startMonitoringForRegion:region];
     }
}

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {

    [self.locationManager performSelector:@selector(requestStateForRegion:) withObject:region afterDelay:5];
}

- (void)locationManager:(CLLocationManager *)manager
  didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {

    if (state == CLRegionStateInside){

        [self enterGeofence:region];

    } else if (state == CLRegionStateOutside){

        [self exitGeofence:region];

    } else if (state == CLRegionStateUnknown){
        NSLog(@"Unknown state for geofence: %@", region);
        return;
    }
}

- (void)enterGeofence:(CLRegion *)geofence {

    //whatever is required when entered
}

- (void)exitGeofence:(CLRegion *)geofence {

    //whatever is required when exit
}

根据此元帖。最好不要在问题本身中包含答案。可以编写单独的答案,接受给定的答案或编写评论。 - mfaani
1个回答

6

不要使用 locationManager:didEnterRegion:,而是使用 locationManager:didDetermineState:forRegion:,因为它可以提供所需的全部信息来触发进入区域的代码。顺便说一下,这个代码不应该是 locationManager:didEnterRegion:,而应该是你自己的选择器,它不是 CLLocationManagerDelegate 协议的一部分。

另一种方法是,在开始监视区域时测试是否在区域内。虽然这种解决方案听起来并不那么简单:您需要先通过调用 startUpdatingLocation 来更新当前位置,因为仅读取 locationManager 的位置属性可能会导致过时或极不准确的读数。


API的意义在于,如果我必须自己跟踪它,那还有什么意义呢?这似乎完全不合逻辑。 - Joe Maher
API描述清楚地说明它监控进入和退出事件。你想得到比API更多的东西,即当从区域内开始监控时触发进入事件。有一个更低级别的API回调函数可以实现这一点,即locationManager:didDetermineState:forRegion:,你可以使用它,但是试图将其与更高级别的内容结合起来会带来麻烦。 - Alex Pavlov
但正如我在问题中提到的那样,当进入或离开区域时,didDetermineState也会被触发。所以你的意思是,在开始监视区域时,我必须手动获取当前位置,如果该位置位于区域内,则自己处理它。忽略requestStateForRegion?我想,如果不使用区域监视,您可能无法使用requestStateForRegion用于其他用途。 - Joe Maher
但是如果您还没有监控该区域,为什么要请求该区域的状态呢? - Joe Maher
这正是我想做的,但就像我说的,当监视该区域时,它会在每次穿过区域边界时调用didDetermineStateForRegion。因此,如果您在此函数内告诉它进入该区域,它将进入该区域两次。抱歉,我觉得我没有很好地解释清楚。 - Joe Maher
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接