【问题标题】:didEnterRegion works in foreground but not background or other VCsdidEnterRegion 在前台工作,但不在后台或其他 VC 中工作
【发布时间】:2012-09-03 19:59:36
【问题描述】:

如果应用程序正在运行并且 CLLocationManagerDelegate 类是前台(即可见),则 didEnterRegions 触发并且我得到 NSLog 和 AlertView。但是,当应用程序在后台时,或者基本上,如果屏幕显示的不是委托类,我什么也得不到。

我已经在 plist 中的“必需的后台模式”下设置了“应用程序注册以进行位置更新”,尽管我不确定这是否有必要。

这就是我认为的相关代码,尽管我可能是错的(并且很乐意添加更多)。我应该注意 viewDidLoad 中的所有内容都包含在 if 中,它检查区域监控是否可用和启用。

- (void)viewDidLoad
{
    NSLog(@"MapViewController - viewDidLoad");
    self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
    self.locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters;    
    self.locationManager.delegate = self;
    [self.locationManager startMonitoringSignificantLocationChanges];
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    NSLog(@"MapViewController - didEnterRegion");
    NSLog(@"MVC - didEnterRegion - region.radius = %f", region.radius);
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"entered region..." message:@"You have Entered the Location." delegate:nil cancelButtonTitle:@"OK"  otherButtonTitles: nil];
    alert.tag = 2;
    [alert show];
}

这是我在 AppDelegate.m 中获得被监控区域列表的地方:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

// other code

NSLog(@"LISTING ALL REGIONS MONITORED");
    NSArray *regions = [self.locationManager.monitoredRegions allObjects];
    if (!regions) {
        NSLog(@"no regions found");
    } else {
        NSLog(@"got %d monitored regions", [regions count]);
        for (int i = 0; i < [regions count]; i++) {
            CLRegion *region = [regions objectAtIndex:i];
            NSLog(@"region %d's identifier = %@", i, region.identifier);
            NSLog(@"region: radius: %@", region.radius);
        }
    }

// other code
}

我调用 startMonitoringForRegion 两次,这里是主要的地方:

- (void)doneButtonTapped {
    NSLog(@"doneButtonTapped");

    if (self.locationIdentifier) {
        if ([CLLocationManager regionMonitoringEnabled] && [CLLocationManager regionMonitoringAvailable]) {

            // core data setup
            NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
            NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"LocationReminder" inManagedObjectContext:self.managedObjectContext];
            fetchRequest.entity = entityDescription;
            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"locationIdentifier == %@", self.locationIdentifier];
            fetchRequest.predicate = predicate;
            NSError *error;
            NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
            if (results) {

                // get the LocationReminder
                LocationReminder *retrievedReminder = [results objectAtIndex:0];
                retrievedReminder.audioURI = [[[self.audioPlayers objectAtIndex:self.selectedCell] url] absoluteString];
                retrievedReminder.userRecording = nil;

                // start monitoring it's region
                NSArray *coordinateArray = [retrievedReminder.locationIdentifier componentsSeparatedByString:@", "];
                CLLocationCoordinate2D coordinate = {[[coordinateArray objectAtIndex:0] doubleValue], [[coordinateArray objectAtIndex:1] doubleValue]};
                CLRegion *newRegion = [[CLRegion alloc] initCircularRegionWithCenter:coordinate radius:250.0 identifier:retrievedReminder.locationIdentifier];
                NSLog(@"about to monitor region with radius: %f", newRegion.radius);
                [self.locationManager startMonitoringForRegion:newRegion desiredAccuracy:kCLLocationAccuracyBest];

                // save the LocationReminder
                if (![self.managedObjectContext save:&error]) {
                    NSLog(@"hmm.  no managed object context.  must be something space-time going on");
                } else {
                    NSLog(@"saved locationReminder, locationIdentifier = %@", retrievedReminder.locationIdentifier);
                }
            } else {
                NSLog(@"ERROR: no LocationReminder retreived for predicate: %@", predicate);
            }
        }

        // get the mapview controller off of the navigation stack
        for (UIViewController *viewController in self.navigationController.viewControllers) {
            if ([viewController isKindOfClass:[MapViewController class]]) { 
                MapViewController *mapVC = (MapViewController *)viewController;
                mapVC.audioURI = [[[self.audioPlayers objectAtIndex:self.selectedCell] url] absoluteString];
                [self.navigationController popToViewController:mapVC animated:YES];
            }
        }
}

因为我觉得它可能很重要,所以这里是 locationManager 的 getter:

- (CLLocationManager *)locationManager {
    NSLog(@"MapViewController - locationManager");
    if (_locationManager) {
        return _locationManager;
    } else {
        _locationManager = [[CLLocationManager alloc] init];
        return _locationManager;
    }
}

更新 1: 通过 Apple 论坛(我在其中交叉发布)有人提到 AlertView 只会显示在前台。 NSLog 仍然没有触发。我假设这应该可行。

【问题讨论】:

  • 能否在调用 startMonitoringForRegion 的位置包含代码?包含的代码现在没有显示,只会触发与重大位置更改相关的回调方法。根据我的经验,这些事件不会从模拟器中触发。

标签: ios cllocationmanager region-management clregion


【解决方案1】:

我的一个朋友写了一篇很好的关于使用地理围栏的教程,这可能有助于解决您遇到的一些问题。

Get started with geofencing

网上和这里都有很多例子。从小处着手,一路向上。一旦开始获取回调,就可以开始将内容扩展到其他视图控制器。

更新

正如 cmets 中所解释的,创建单例类来控制您的位置管理器和委托方法的好处。通过使用单例,您可以防止多次调用您的委托方法。您可以通过仔细编码来防止这种情况,但使用单例可以为您做到这一点。这也是一个很好的类,可以处理委托方法需要完成的所有工作。

【讨论】:

  • 感谢您的链接。我很确定我已经涵盖了所有这些基础。在这一点上,我认为更多的是关于应用程序如何进入和退出后台,而不是区域监控。事实上,对我来说,它只在 CLLocationManagerDelegate 中有效——didEnterRegion 应该如何被调用? iOS 是否知道足以唤醒应用程序并实例化正确的 VC?我想知道它是否与 UIApplicationDelegate 协议有关。
  • 另一方面,位置感知编程指南中根本没有提到 UIApplicationDelegate,这让我认为它并不重要。仍然对操作系统应该如何知道 didEnterRegion 在哪里实现以及如果应用程序在后台如何访问该类感到困惑......
  • 屏幕关闭或进入后台,你应该确保设置你的位置管理代理,以防它没有设置。我在 -applicationDidEnterBackground 中将委托设置为我的 locationController,这应该涵盖背景和屏幕关闭事件。
  • 对于区域监控我也不使用。一旦你开始监视一个区域,你应该很高兴。这是区域监控的好处之一。您也不必注册后台位置。
【解决方案2】:

你做错的事情:

  1. 后台模式 - 应用程序注册位置更新。这不是必需的。当您想要收集有关位置等重大变化的信息时,这是需要的。因此,请转到 Targets > Your app > Capabilites ,然后在背景模式下选择所需的选项。这将自动为您更新 plist。现在,禁用它。
  2. 您正在尝试在用户进入区域时创建警报。虽然这在应用程序运行时工作,但当您的应用程序处于后台时,警报是没有用的。 - 而是触发本地通知或 api 调用。

例如。通知:

    -(void)triggerLocalNotification:(CLRegion *)region{
    UILocalNotification *notification = [[UILocalNotification alloc]init];
    [notification setAlertBody:[NSString stringWithFormat:@"Welcome to %@", [region identifier]]];
    [notification setRepeatInterval:0];
    [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:2]];
    [notification setTimeZone:[NSTimeZone  defaultTimeZone]];
    [[UIApplication sharedApplication]scheduleLocalNotification:notification];
    NSLog(@"notification triggered with notification %@", notification);
}

【讨论】:

  • startMonitoringSignificantLocationChanges 和 startMonitoringForRegion 是否在后台模式下工作?
  • 如果您正在执行 startMonitoringSignificantLocationChanges,那么是的,您需要后台模式。当位置发生显着变化时,您需要唤醒您的应用程序,并且您需要将其告知您的系统。但是你不需要它来启动监控区域。无论后台模式如何,startMonitoringForRegion 都会调用 didEnterRegion 和 didExitRegion。
  • 非常感谢,我想以最少的电池消耗使用 CLLocationManager。
【解决方案3】:

您可以在执行EnterRegion 时发布本地通知。

即使您在后台,也会显示类似警报的弹出窗口。

你可以做一个简单的测试:

1) 在应用委托的 applicationDidEnterBackground 中创建一个本地通知对象,其中包含任何随机消息,并告诉本地通知立即触发。

2) 按下主页按钮,当您最小化应用程序时,您应该会看到一个弹出窗口。

【讨论】:

  • 感谢您的建议 - 我不熟悉 UIApplicationDelegate 协议,现在我想知道这是否不是问题所在。看看下面我给 Bill Burgess 的 cmets。
【解决方案4】:

我认为你需要转到你的 app.plist

并添加必需的背景模式:添加 itme 应用程序寄存器以进行位置更新

和 1 。如果您的应用在后台,您仍然会看到顶部的箭头

还有2,如果app被杀了,你仍然可以在顶部看到一个空心箭头,ios会为你监控区域,但限制在20个区域

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-15
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多