【问题标题】:CLLocationManager monitoredRegions (NSSet) is not correct, or maybe something else?CLLocationManager 监控区域(NSSet)不正确,或者其他什么?
【发布时间】:2016-05-11 15:29:36
【问题描述】:

我想在用户每次通过商店数组时检查商店,我有 20 多家商店,所以我编写了一个函数来查找离用户位置最近的 20 家商店并监控它们。 locationManager: didUpdateLocations 更新列表,我还将旧的 20 个监控区域替换为新的 20 个最近的商店位置。

问题是locationManager: didEnterRegion在用户进入区域时没有被定期调用。

我还注意到,当我检查 locationManager.monitoredRegions NSSet 时,由于某种原因,这些区域有误(我用 if 句子检查了它,所以它们可能是正确的并且更短?)。

如果有人可以检查我的代码并可能注意到我做错了什么,那真的会帮助我!

我的代码:

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

-(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;
}

注意:委托方法正在被调用,即使是 didEnterRegion:,但由于某种原因并非总是如此。

解决了问题:

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

 -(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;
            }
        }
    }

非常感谢!

【问题讨论】:

  • 请注意,Apple 文档声明添加 CLRegion 进行监控是异步完成的,您还可以通过实现委托方法来验证该区域是否已添加:- locationManager:didStartMonitoringForRegion:
  • @FS.O,您仍然没有考虑到添加区域是异步完成的。
  • @FS.O,如果您查看内联文档,您会看到 Apple 说:这是异步完成的,可能不会立即反映在受监控的区域中。这意味着当你添加一个区域时,它可能不会立即添加,而是异步操作完成时。
  • 在我的回复中,我给了你很多思考。如果您有后续问题,请告诉我。不要忘记投票并让每个人确切地知道您如何在用例中解决问题(通过更新编辑您的问题)。这将帮助下一个遇到此问题的人。如果它解决了问题,请使用答案上的复选标记接受答案,但更重要的是通过更新您的问题让我们知道适合您的解决方案。这将鼓励其他人回答您未来的问题。
  • @TommieC。我会的,谢谢

标签: ios objective-c cocoa-touch cllocationmanager nsset


【解决方案1】:

区域半径

请查看下面关于 startMonitoringForRegion 方法的讨论,并注意 Apple 表示区域值高达 400 最适合以后的设备。他们还表明,人们平均可以在 3 到 5 分钟内得到回复。我不确定您是如何测试该区域的进入/退出的,但可能是您没有允许足够的时间通过。

区域监控

此外,我建议您设置断点以在代码中的战略点(当您希望区域更新时)检查 CLLocationManager 的 monitoredRegions 属性。在使用自定义代码重新填充区域之前,您可能会考虑清空区域数组(请参阅下面的 Namely: header)。我会非常仔细地检查该区域,以确定预期的行为正在发生。

过滤器

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

命中测试用户位置

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

位置变化的背景更新

我建议您仔细阅读与startMonitoringSignificantLocationChanges 方法相关的讨论(您必须使用此方法来解决问题)。 Apple 详细说明了当应用程序处于后台时获取更新所需的步骤。基本上你必须在你的应用委托方法中截取一个键。幸运的是,SO question 有示例代码。但是在你跳到代码之前,请按照推荐的方式查看讨论,因为它会让你更清楚地了解如何掌握这种行为。

部分答案

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

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

即:

Useful responses on how to refine geofencing

A simplified tutorial of geofencing

(您是否在位置管理器上使用了 requestAlwaysAuthorization()?)

Nota Bene

我建议您阅读有关方法和属性的讨论,以查看来自 Apple 的提示。您还应该随时在代码编辑器中单击 cmd 并单击方法和属性,并查看头文件中的 cmets。这些将提供来自 Apple 的更多见解,而这些见解在文档或快速帮助中是不可用的。

作为一个简单的示例,以下标题 cmets 似乎与您的目标相关。您可能希望实现和监控注释头中提到的委托方法和 requestStateForRegion 方法:

/*
 *  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)

【讨论】:

    猜你喜欢
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 2012-01-25
    • 2012-06-11
    • 2012-03-22
    • 2018-03-05
    • 2016-05-02
    • 1970-01-01
    相关资源
    最近更新 更多