CLLocationManager monitoredRegions(NSSet)不正确,还是其他什么?
我想检查每次用户从一系列商店通过商店时,我有超过20个商店,所以我写了一个function,找到距离用户位置最近的20个商店并监控它们。 在locationManager: didUpdateLocations
上更新的列表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 *)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&¤tStore.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 *)locations { [self checkIfNearStore]; //Not being called in background } -(void)checkIfNearStore { for (Store *currentStore in self.allStores) { if ([currentStore.circularRegion containsCoordinate:self.locationManager.location.coordinate]&¤tStore.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; } } }
非常感谢你!
区域半径
请查看下面关于startMonitoringForRegion方法的讨论,并注意Apple指出最多400个区域的值在以后的设备上效果最佳。 他们还指出,人们可以在平均3到5分钟内得到答复。 我不确定你是如何测试该地区的出入口,但可能是你没有足够的时间通过。
区域监测
另外,我建议你设置断点来检查代码中关键点的CLLocationManager的monitoredRegions属性(当你希望更新区域时)。 在使用自定义代码重新填充之前,您可以考虑清空regions数组(请参阅下面的Namely:标题 )。 我会仔细检查那个区域,以确保预期的行为正在发生。
filter
当位置发生重大变化时,有许多filter告诉CLLocationManager。 您应该设置这些值并检查它们是否对更新频率有影响。 另请注意我展示的其他属性和方法,包括desiredAccuracy和启动重要更新的方法。 阅读每个讨论的讨论,看看它们是否适用于您的用例。 尝试在需要时应用更改,以便在适合您的情况下强制设备更新。
点击测试用户位置
您可能希望使用用户位置实现特定CLCircularRegion的定期命中测试。 这应该允许您在代码中的预定时间/位置进一步细化位置更新。 例如,您可能希望使用精度和距离filter优化用户更新,然后在委托方法中调用针对arrays中最近区域的查询。 净效应是在设备从最后的已知位置显着移动时强制进行区域状态检查。
位置更改的后台更新
我建议您仔细阅读与startMonitoringSignificantLocationChanges方法相关的讨论(您必须使用此方法来解决此问题)。 Apple详细介绍了在应用程序处于后台时获取更新所需的步骤。 基本上,您必须在应用委托方法中拦截一个键。 幸运的是,这个SO问题有示例代码。 但在您跳转到代码之前,请按照建议进行讨论,因为它可以让您更清楚地了解如何掌握此行为。
部分答案
像许多问题一样。 这个需要一些部分答案来解决您需要的function。 首先确保您已经检查了上面提供的注释,然后尝试使用这些链接来获得特定所需任务的部分答案。
如何禁用使用-startMonitoringForRegion注册的任何CLRegion对象?
即:
关于如何优化地理围栏的有用回应
地理围栏的简化教程
(您是否在位置管理器上使用了requestAlwaysAuthorization() ?)
Nota Bene
我建议您阅读方法和属性的讨论,以查看Apple的提示。 您还应该可以自由地单击代码编辑器中的方法和属性,并查看头文件中的注释。 这些将提供Apple提供的更多信息,这些信息在文档或快速帮助中不可用。
作为一个简单的示例,以下标题注释似乎与您的目标相关。 您可能希望实现并监视注释标头中提到的委托方法和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)