CLLocationManager的monitoringRegions(NSSet)不正确,或者可能是别的什么? [英] CLLocationManager monitoredRegions (NSSet) is not correct, or maybe something else?

查看:365
本文介绍了CLLocationManager的monitoringRegions(NSSet)不正确,或者可能是别的什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想检查每次用户从商店数组传递商店,我有20多家商店,所以我写了一个函数,找到20个最接近的商店到用户的位置,并监控他们。在 locationManager:didUpdateLocations 上更新的列表,我还用新的20个最近的商店位置替换旧的20个受监控区域。



问题是,当用户输入一个区域时, locationManager:didEnterRegion 没有被定期调用。



我也注意到,当我检查 locationManager.monitoredRegions NSSet 的地区有错误的原因我用检查它,如果句子,所以也许他们是正确的,只是更短?)





我的代码:



strong> monitorLocationViewController.m (滚动查看完整代码):

   - (void)getStoresArrays: *)通知
{
//获取allStoresArray(使用NSNotificationCenter从另一个类发送的所有商店的NSArray)并将其添加到allStores(NSMutableArray)
NSDictionary * storesCategoriesArrays =通知用户信息];
self.allStores = [storesCategoryArrays objectForKey:@allStoresArray];

//调用locationChangeHandler监视
[self locationChangeHandler];
}

#pragma mark - CLLocationDelegate方法
//用户位置更新时调用
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray< ; CLLocation *> *)locations
{
//如果allStores(NSMutableArray)不是nil - 调用locationChangeHandler更新监视
if(self.allStores!= nil ){
[self locationChangeHandler];
}
}
//当用户进入受监视区域时被调用
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(@Enterered);
}

#pragma mark - 最近的存储排序方法
//将最近的存储排序到用户的位置,并将最近的存储添加到twentyClosestStores(NSMutableArray)
- (void)sortClosestStores
{
//从最近的Store将allStores(NSMutableArray)排序到最远。
[self.allStores sortUsingComparator:^ NSComparisonResult(id _Nonnull obj1,id _Nonnull obj2){
//创建location1(CLLocation)和location2(CLLocation),并用obj1 id)和obj2(id)坐标
CLLocation * location1 = [[CLLocation alloc] initWithLatitude:(Store *)obj1).geoPoint.latitude longitude:((Store *)obj1).geoPoint.longitude] ;
CLLocation * location2 = [[CLLocation alloc] initWithLatitude:((Store *)obj2).geoPoint.latitude longitude:((Store *)obj2).geoPoint.longitude];

//创建dist1(float)并将其值设置为location1(CLLocation)和用户位置之间的距离
float dist1 = [location1 distanceFromLocation:self.locationManager。位置];
//创建dist2(float)并将其值设置为location2(CLLocation)和用户位置之间的距离
float dist2 = [location2 distanceFromLocation:self.locationManager.location];

//如果距离相等 - 顺序将保持不变
if(dist1 == dist2){
return NSOrderedSame;
}
else if(dist1 return NSOrderedAscending;
}
else {// else - dist2(float)将在数组中的dist1之前
return NSOrderedDescending;
}
}];

//如果twentyClosestStores(NSMutableArray)是nil
if(self.twentyClosestStores == nil){
//初始化twentyClosestStores(NSMutableArray)
self.twentyClosestStores = [NSMutableArray array];
}

//如果previousTwentyStores(NSMutableArray)是nil
if(self.previousTwentyStores == nil){
//初始化previousTwentyStores(NSMutableArray )
self.previousTwentyStores = [NSMutableArray array];
}
//将previousTwentyStores(NSMutableArray)设置为twentyClosestStores(NSMutableArray)
self.previousTwentyStores = self.twentyClosestStores;
//清理(reInitializing)twentyClosestStores(NSMutableArray)
self.twentyClosestStores = [NSMutableArray array];

//将allStores(NSMutableArray)(与用户当前位置最接近的商店的20个)的索引0-19添加到twentyClosestStores(NSMutableArray)
for(int i = 0; i <20; i ++){
[self.twentyClosestStores addObject:[self.allStores objectAtIndex:i]];
}
}

#pragma mark - 开始/停止监视方法
//用于更新监视
- (void)locationChangeHandler
{
//如果allStores(NSMutableArray)不是nil
if(self.allStores!= nil){
//查找与用户位置最接近的20个商店,并将其添加到twentyClosestStores(NSMutableArray)
[self sortClosestStores];
//停止监视previousTwentyStores(NSMutableArray)(在用户位置更新之前最近的20个商店)
[self stopMonitoringStores];
//开始监视twentyClosestStores(NSMutableArray)
[self startMonitoringClosestStores];
}
}
//开始监视twentyClosestStores(NSMutableArray)
- (void)startMonitoringClosestStores
{
//如果监视不可用对于CLCircularRegion
if(![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]){
NSLog(@监视不可用于CLCircularRegion类
}

//在所有twentyClosestStores(NSMutableArray)的对象上运行
for(Store * currentStore in self.twentyClosestStores){
//创建区域(CLCircularRegion)并将其设置为currentStore(Store)的圆形区域
CLCircularRegion * region = [currentStore createCircularRegion];

//开始监视region(CLCircularRegion)
[self.locationManager startMonitoringForRegion:region];
}
}
//停止监视previousTwentyStores(NSMutableArray)(在用户位置更新之前最近的20个商店)
- (void)stopMonitoringStores
{
//运行所有previousTwentyStores(NSMutableArray)的对象
for(Store * currentStore in self.previousTwentyStores){
//创建region(CLCircularRegion)并将其设置为currentStore (Store)的圆形区域
CLCircularRegion * region = [currentStore createCircularRegion];

//停止监视region(CLCircularRegion)
[self.locationManager stopMonitoringForRegion:region];
}
}

//查找区域的商店
- (Store *)storeForRegion:(CLCircularRegion *)region
{
//创建latitude(CGFloat)和longtitude(CGFloat)并将其设置为region(CLCircularRegion)的center.latitude / longtitude
CGFloat latitude = region.center.latitude;
CGFloat longtitude = region.center.longitude;

//运行所有的allStores(NSMutableArray)的对象
for(Store * currentStore in self.allStores){

//如果currentStore (Store)的纬度和经度等于纬度(CGFloat)和longtitude(CGFloat)
if(currentStore.geoPoint.latitude == latitude&& currentStore.geoPoint.longitude == longtitude){
//返回currentStore(Store)
return currentStore;
}
}
//未找到存储 - 返回nil
NSLog(@未找到此区域的存储:%@,[region description]);
return nil;
}

Store.m

  //创建并返回商店的CLCircularRegion对象
- (CLCircularRegion *)createCircularRegion
{
//创建region(CLCircularRegion)并使用当前存储信息(self)和无线电200m初始化它
CLCircularRegion * region = [[CLCircularRegion alloc] initWithCenter:self.geoPoint radius:200 identifier:self.identifier ];

//将region(CLCircularRegion)的notifyOnEntry设置为YES
region.notifyOnEntry = YES;

//返回region(CLCircularRegion)
返回区域;
}

注意:委托方法正在被调用,即使



(我决定不使用区域监控并自己做)

   - )locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray< CLLocation *> *)locations 
{
[self checkIfNearStore] //不在后台调用
}

- (void)checkIfNearStore
{
for(Store * currentStore in self.allStores){
if ([currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]&&& currentStore.alreadySendNotification == NO){
NSLog(@Entered:%@,[[self storeForRegion:currentStore.circularRegion]地址]);
currentStore.alreadySendNotification = YES;
[self.storesAlreadySentNotifications addObject:currentStore];
}
}

for(Store * currentStore in self.storesAlreadySentNotifications){
if(![currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate] ){
currentStore.alreadySendNotification = NO;
}
}
}

非常感谢! / p>

解决方案

区域半径



请参阅 startMonitoringForRegion 方法,请注意,Apple表示最多400个区域的值在后面的设备上效果最好。他们还表明,人们可以在3到5分钟内平均预期反应。我不知道您是如何测试该区域的进入/退出,但可能是您没有足够的时间通过。





区域监控



此外,我建议您设置断点以检查 monitoringRegions 属性CLLocationManager在你的代码的战略点(当你希望区域更新)。您可以考虑清空regions数组(参见下面的即:header ),然后再使用自定义代码重新填充它们。我会非常仔细地检查该区域,以确保预期的行为正在发生。





过滤器



有一些过滤器告诉CLLocationManager当位置发生重大变化时。您应该设置这些值并检查它们是否对更新的频率有影响。另请注意我正在显示的其他属性和方法,包括 desiredAccuracy 以及启动重要更新的方法。阅读每一个的讨论,看看它们是否适用于您的用例。尝试在需要时应用更改,以强制设备在适当情况下进行更新。





点击测试用户位置



您可能希望使用用户位置实施特定CLCircularRegion的定期匹配测试。这应该允许您在代码中的预定时间/位置进一步细化位置更新。例如,您可能需要使用精度和距离过滤器细化用户更新,然后在委托方法中调用针对数组中最近区域的查询。最终效果是每当设备从最后一个已知位置显着移动时强制执行区域状态检查。



方法(您必须使用此方法来解决问题)。 Apple详细说明了当应用程序处于后台时,您需要获取更新的步骤。基本上你将不得不在你的应用程序委托方法中拦截一个键。幸运的是这



部分答案



很多问题。这一个需要多个部分答案来解决您需要的功能。首先请确保您已经检查了上面提供的注释,然后尝试一些此链接以获取特定所需任务的部分答案。





如何改善地理围栏功能的实用回应



地理围栏的简化教程



(您在位置管理器上使用 requestAlwaysAuthorization()吗?)



Nota Bene



我建议您阅读方法和属性的讨论,以查看来自Apple的提示。您还应该随时在代码编辑器中cmd方法和属性,并查看头文件中的注释。这些将提供更多的知识从苹果,在文档或快速帮助中没有。



作为一个快速示例,以下标题评论似乎与您的目标相关。您可能希望实现和监视注释标题中提到的委托方法和requestStateForRegion方法:

  / * 
* locationManager:didDetermineState:forRegion:
*
*讨论:
*当受监视区域有状态转换或通过
* a响应状态请求时调用调用requestStateForRegion :.
* /
@available(iOS 7.0,*)
可选public func locationManager(manager:CLLocationManager,didDetermineState state:CLRegionState,forRegion region:CLRegion)


I want to check every time the user passes a store from an array of stores, I have more than 20 stores so I wrote a function that finds the 20 closest stores to the user’s location and monitors them. The list being updated on locationManager: didUpdateLocations, I also replace the old 20 monitored regions with the new 20 closest stores location.

The problem is that locationManager: didEnterRegion isn’t being called regularly when the user enters a region.

I’ve also noticed that when I check the locationManager.monitoredRegions NSSet the regions there are wrong for some reason (I checked it with if sentence so maybe they are correct and just shorter?).

If someone could check my code and maybe notice something I done wrong, It’ll really help me!

My code:

monitorLocationViewController.m(scroll to see the full code):

-(void)getStoresArrays:(NSNotification*)notification
{
    //Fetching "allStoresArray"(NSArray of all the stores sent from another class using NSNotificationCenter) and adding it to "allStores"(NSMutableArray)
    NSDictionary *storesCategoriesArrays=[notification userInfo];
    self.allStores=[storesCategoriesArrays objectForKey:@"allStoresArray"];

    //Calling "locationChangeHandler" for monitoring
    [self locationChangeHandler];
}

#pragma mark - CLLocationDelegate methods
//Being called when user's location updated
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    //If "allStores"(NSMutableArray) isn't nil - calling "locationChangeHandler" to update monitoring
    if (self.allStores!=nil) {
        [self locationChangeHandler];
    }
}
//Being called when user enters a monitored region
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    NSLog(@"Entered");
}

#pragma mark - Closest stores sorting methods
//Sorting closest stores to the user's location and adding the 20 closest store to "twentyClosestStores"(NSMutableArray)
-(void)sortClosestStores
{
    //Sorting "allStores"(NSMutableArray) from the closest "Store" to the furthest.
    [self.allStores sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        //Creating "location1"(CLLocation) and "location2"(CLLocation) and initializing each with "obj1"(id) and "obj2"(id) coordinates
        CLLocation *location1=[[CLLocation alloc] initWithLatitude:((Store*)obj1).geoPoint.latitude longitude:((Store*)obj1).geoPoint.longitude];
        CLLocation *location2=[[CLLocation alloc] initWithLatitude:((Store*)obj2).geoPoint.latitude longitude:((Store*)obj2).geoPoint.longitude];

        //Creating "dist1"(float) and setting its value to the distance between "location1"(CLLocation) and the user's location
        float dist1 =[location1 distanceFromLocation:self.locationManager.location];
        //Creating "dist2"(float) and setting its value to the distance between "location2"(CLLocation) and the user's location
        float dist2 = [location2 distanceFromLocation:self.locationManager.location];

        //If the distances are equal - the order will stay the same
        if (dist1 == dist2) {
            return NSOrderedSame;
        }
        else if (dist1 < dist2) { //If "dist1"(float) is smaller than "dist2"(float) - "dist2"(float) will be before "dist1" in the array
            return NSOrderedAscending;
        }
        else { //else - "dist2"(float) will be before "dist1" in the array
            return NSOrderedDescending;
        }
    }];

    //If "twentyClosestStores"(NSMutableArray) is nil
    if (self.twentyClosestStores==nil) {
        //Initializing "twentyClosestStores"(NSMutableArray)
        self.twentyClosestStores=[NSMutableArray array];
    }

    //If "previousTwentyStores"(NSMutableArray) is nil
    if (self.previousTwentyStores==nil) {
        //Initializing "previousTwentyStores"(NSMutableArray)
        self.previousTwentyStores=[NSMutableArray array];
    }
    //Setting "previousTwentyStores"(NSMutableArray) to "twentyClosestStores"(NSMutableArray)
    self.previousTwentyStores=self.twentyClosestStores;
    //Cleaning (reInitializing) "twentyClosestStores"(NSMutableArray)
    self.twentyClosestStores=[NSMutableArray array];

    //Adding indexes 0-19 of "allStores"(NSMutableArray) (20 closest stores to the user's current location) to "twentyClosestStores"(NSMutableArray)
    for (int i = 0; i < 20; i++) {
        [self.twentyClosestStores addObject:[self.allStores objectAtIndex:i]];
    }
}

#pragma mark - Start/stop monitoring methods
//For updating monitoring
-(void)locationChangeHandler
{
    //If "allStores"(NSMutableArray) isn't nil
    if (self.allStores!=nil) {
        //Finding the 20 closest stores to he user's location and adding it to "twentyClosestStores"(NSMutableArray)
        [self sortClosestStores];
        //Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location  updated)
        [self stopMonitoringStores];
        //Start monitoring "twentyClosestStores"(NSMutableArray)
        [self startMonitoringClosestStores];
    }
}
//Start monitoring "twentyClosestStores"(NSMutableArray)
-(void)startMonitoringClosestStores
{
    //If monitoring isn't availible for "CLCircularRegion"
    if (![CLLocationManager isMonitoringAvailableForClass:[CLCircularRegion class]]) {
        NSLog(@"Monitoring is not available for CLCircularRegion class");
    }

    //Run on all "twentyClosestStores"(NSMutableArray)'s objects
    for (Store *currentStore in self.twentyClosestStores) {
        //Creating "region"(CLCircularRegion) and setting it to "currentStore"(Store)'s circular region
        CLCircularRegion *region=[currentStore createCircularRegion];

        //Start monitoring "region"(CLCircularRegion)
        [self.locationManager startMonitoringForRegion:region];
    }
}
//Stop monitoring "previousTwentyStores"(NSMutableArray) (20 closest stores before user's location  updated)
-(void)stopMonitoringStores
{
    //Run on all "previousTwentyStores"(NSMutableArray)'s objects
    for (Store *currentStore in self.previousTwentyStores) {
        //Creating "region"(CLCircularRegion) and setting it to "currentStore"(Store)'s circular region
         CLCircularRegion *region=[currentStore createCircularRegion];

        //Stop monitoring "region"(CLCircularRegion)
        [self.locationManager stopMonitoringForRegion:region];
    }
}

//Finding a store for region
-(Store*)storeForRegion:(CLCircularRegion*)region
{
    //Creating "latitude"(CGFloat) and "longtitude"(CGFloat) and setting it to "region"(CLCircularRegion)'s center.latitude/longtitude
    CGFloat latitude=region.center.latitude;
    CGFloat longtitude=region.center.longitude;

    //Run on all "allStores"(NSMutableArray)'s objects
    for (Store *currentStore in self.allStores) {

        //If "currentStore"(Store)'s latitude and longtitude is equal to "latitude"(CGFloat) and longtitude(CGFloat)
        if (currentStore.geoPoint.latitude==latitude&&currentStore.geoPoint.longitude==longtitude) {
            //Returning "currentStore"(Store)
            return currentStore;
        }
    }
    //Store not found - returning nil
    NSLog(@"No store found for this region: %@",[region description]);
    return nil;
}

Store.m:

    //Creating and returning a "CLCircularRegion" object of the store
-(CLCircularRegion*)createCircularRegion
{
    //Creating "region"(CLCircularRegion) and initializing it with current store information (self) and radios of 200m
    CLCircularRegion *region=[[CLCircularRegion alloc] initWithCenter:self.geoPoint radius:200 identifier:self.identifier];

    //Setting "region"(CLCircularRegion)'s notifyOnEntry to YES
    region.notifyOnEntry=YES;

    //Returning "region"(CLCircularRegion)
    return region;
}

Note: The delegate methods are being called, even didEnterRegion: but not always for some reason.

Solved the problem:

(I've decided not to use region monitoring and to do it myself)

 -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    [self checkIfNearStore]; //Not being called in background
}

-(void)checkIfNearStore
    {
        for (Store *currentStore in self.allStores) {
            if ([currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]&&currentStore.alreadySendNotification==NO) {
                NSLog(@"Entered: %@",[[self storeForRegion:currentStore.circularRegion] address]);
                currentStore.alreadySendNotification=YES;
                [self.storesAlreadySentNotifications addObject:currentStore];
            }
        }

        for (Store *currentStore in self.storesAlreadySentNotifications) {
            if (![currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]) {
                currentStore.alreadySendNotification=NO;
            }
        }
    }

Thank you very much!

解决方案

Region Radius

Please review the discussion on the startMonitoringForRegion method below and note that Apple indicates that a value for the region up to 400 works best on later devices. They also indicate that one can expect responses within 3 to 5 mins on average. I am not sure how you are testing the entry/exit of the region but it may be that you are not allowing sufficient time to pass.

Region Monitoring

Additionally, I suggest you setup break points to examine the monitoredRegions property of the CLLocationManager at strategic points in your code (when you expect the regions to update). You might consider emptying the regions array (see the Namely: header below) before repopulating them with your custom code. I'd inspect that area very carefully to be certain that the expected behavior is occurring.

Filters

There are a number of filters that tell the CLLocationManager when a significant change in position has occurred. You should set these values and inspect to see whether they have an impact on the frequency of the updates. Please also note the other properties and methods that I am showing including the desiredAccuracy and the methods for initiating significant updates. Read the discussion for each of these to see whether they apply in your use case. Try to apply changes where needed to force the device to update when it is appropriate in your case.

Hit Testing User Location

You might want to implement periodic hit-testing of the specific CLCircularRegion using the user location. This should allow you to further refine location updates at predetermined times/locations in your code. For example you might want to refine the user updates using accuracy and distance filters and then in the delegate method call a query against the closest region(s) in your array. The net effect would be to force a region status check whenever the device moves significantly from the last known position.

Background Updates for Location Changes

I recommend you give a close re-read of the discussion related to the startMonitoringSignificantLocationChanges method (You must use this method to resolve the issue). Apple details the steps that you need to get the updates when the app is in the background. Basically you will have to intercept a key in your app delegate method. Fortunately this SO question has sample code. But before you jump to the code, please review the discussion as recommended since it will give you a much clearer idea of how to master this behavior.

Partial Answers

Like many questions. This one requires a number of partial answers to resolve the functionality you need. First make sure you have checked the notes provided above, then try some of this links for partial answers to specific required tasks.

How can I disable any CLRegion objects registered with -startMonitoringForRegion?

Namely:

Useful responses on how to refine geofencing

A simplified tutorial of geofencing

(Did you use requestAlwaysAuthorization() on the location manager?)

Nota Bene

I recommend that you read the discussions of the methods and properties to see hints from Apple. You should also feel free to cmd-click on the methods and properties in code editor and review the comments in the header files. These will provide more insight from Apple that is not available in the documentation or Quick Help.

As a quick example the following header comments seem relevant to your objective. You might want to implement and monitor the delegate method and the requestStateForRegion method mentioned in the comment header:

/*
 *  locationManager:didDetermineState:forRegion:
 *
 *  Discussion:
 *    Invoked when there's a state transition for a monitored region or in response to a request for state via a
 *    a call to requestStateForRegion:.
 */
@available(iOS 7.0, *)
optional public func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion)

这篇关于CLLocationManager的monitoringRegions(NSSet)不正确,或者可能是别的什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆