关于cllocationmanager:iOS Geofence,监控开始时在区域内如何处理?

iOS Geofence, how to handle when inside region when monitoring starts?

当调用 startMonitoringForRegion 时,我一直无法弄清楚如何处理手机已经在区域内的情况?其他问题建议在 didStartMonitoringForRegion 中调用 requestStateForRegion,然后调用方法 didDetermineState: forRegion:。所以代码看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (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 方法是我自己的,它工作正常,并且对象包含诸如 lat long 和 radius 之类的东西,而且一切正常,所以这不是这里的问题。

无论如何,上述代码的问题在于,如果用户在将区域添加到他们的设备时已经在该区域内(即,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,但随后会再次调用它,因为根据 Apple 文档,还会自动调用 didDetermineState: forRegion:,这会导致再次调用 didEnterRegion 所以当我只想输入一次时,该区域被输入了两次。我怎样才能避免这种情况?

感谢您的帮助。

解决方案

解决方案真的很简单,我只是以错误的方式去做。我必须选择使用两种方法 didEnterRegion:didExitRegion 或使用 didDetermineState: forRegion 并创建我自己的方法来进入和退出该区域,两者都不应该使用。

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

请注意,如果区域不在内部,则使用此方法将调用退出区域,如果像我一样,您只想在输入发生后退出,您将需要某种方法来检查该区域是否有已经输入(我自己使用核心数据,因为我已经使用它来存储区域的其他方面)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (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: 为您提供了触发 on-entry 代码所需的所有信息,顺便说一下,它不应该是 locationManager:didEnterRegion: ,使用您自己的选择器,它不是 CLLocationManagerDelegate 协议的一部分。

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