iOS地理围栏,开始监控时进入区域内如何处理?
iOS Geofence, how to handle when inside region when monitoring starts?
我无法弄清楚如何处理调用 startMonitoringForRegion
时 phone 已经在区域内的情况?其他问题建议在 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:
,根据苹果文档:
The location manager calls this method whenever there is a boundary transition for a region. It calls this method in addition to calling the locationManager:didEnterRegion: and locationManager:didExitRegion: methods. The location manager also calls this method in response to a call to its requestStateForRegion: method, which runs asynchronously.
因此,每次进入区域时,都会自动调用 didEnterRegion
,但随后会再次调用,因为根据苹果文档,didDetermineState: forRegion:
也会自动调用,这会导致 didEnterRegion
被再次调用,所以当我只想输入一次时,该区域被输入了两次。我怎样才能避免这种情况?
感谢您的帮助。
解决方案
解决方案真的很简单,我只是用错误的方式解决了它。我不得不选择使用 didEnterRegion:
和 didExitRegion
这两种方法,或者使用 didDetermineState: forRegion
并创建我自己的进入和退出区域的方法, 两者都不应该使用。
所以我选择只使用 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 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
}
根本不要使用 locationManager:didEnterRegion:,因为 locationManager:didDetermineState:forRegion: 为您提供了所需的所有信息触发入口代码,顺便说一下,它不应该是 locationManager:didEnterRegion:,使用你自己的选择器,它不是 CLLocationManagerDelegate 的一部分 协议。
另一种方法是在开始监视区域时测试区域内位置。这个解决方案并不像听起来那么简单:您需要首先通过调用 startUpdatingLocation 来更新当前位置,因为仅读取 locationManager 的位置 属性 可能会给您陈旧或极端读数不准确。
我无法弄清楚如何处理调用 startMonitoringForRegion
时 phone 已经在区域内的情况?其他问题建议在 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:
,根据苹果文档:
The location manager calls this method whenever there is a boundary transition for a region. It calls this method in addition to calling the locationManager:didEnterRegion: and locationManager:didExitRegion: methods. The location manager also calls this method in response to a call to its requestStateForRegion: method, which runs asynchronously.
因此,每次进入区域时,都会自动调用 didEnterRegion
,但随后会再次调用,因为根据苹果文档,didDetermineState: forRegion:
也会自动调用,这会导致 didEnterRegion
被再次调用,所以当我只想输入一次时,该区域被输入了两次。我怎样才能避免这种情况?
感谢您的帮助。
解决方案
解决方案真的很简单,我只是用错误的方式解决了它。我不得不选择使用 didEnterRegion:
和 didExitRegion
这两种方法,或者使用 didDetermineState: forRegion
并创建我自己的进入和退出区域的方法, 两者都不应该使用。
所以我选择只使用 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 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
}
根本不要使用 locationManager:didEnterRegion:,因为 locationManager:didDetermineState:forRegion: 为您提供了所需的所有信息触发入口代码,顺便说一下,它不应该是 locationManager:didEnterRegion:,使用你自己的选择器,它不是 CLLocationManagerDelegate 的一部分 协议。
另一种方法是在开始监视区域时测试区域内位置。这个解决方案并不像听起来那么简单:您需要首先通过调用 startUpdatingLocation 来更新当前位置,因为仅读取 locationManager 的位置 属性 可能会给您陈旧或极端读数不准确。