从 iBeacon 接近控制视图

Posted

技术标签:

【中文标题】从 iBeacon 接近控制视图【英文标题】:Controlling view from iBeacon proximity 【发布时间】:2013-11-22 14:55:39 【问题描述】:

我正在使用 Corelocation 和 iBeacons。我在进入和离开一个区域时触发了通知,我可以对我的信标进行范围等。这一切都很好。

但是我陷入了混乱。我想在靠近特定信标时加载第二个视图,然后在我们离开该信标时关闭该视图,冲洗并重复第二个信标。

我正在努力:

如何停止触发视图变化,因为测距没有停止。如果我手动停止测距或使用 bool 来测试我是否已经在第二个视图中,这没关系,但看起来很乱。

如果我离开信标,如何关闭视图。要做到这一点,我想我不能停止测距,否则我不知道我是否搬走了。

我的代码如下。

    - (void)viewDidLoad

    [super viewDidLoad];



- (void)viewDidAppear:(BOOL)animated 

    // Setup Beacon Manager
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    NSUUID *uuid = [[NSUUID alloc]initWithUUIDString:@"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"Beacon Region"];
    [self.locationManager startMonitoringForRegion:self.beaconRegion];
    [self.locationManager requestStateForRegion:self.beaconRegion];
    self.beaconStatLabel.text = @"StartLocationServices";



    //check to see if this is the first time we've run the app
    if ([[NSUserDefaults standardUserDefaults] floatForKey:@"tBHasRun"] == 0) 
        [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"tBHasRun"];  //set the time run to 1
        [self performSegueWithIdentifier:@"firstRunSegue" sender:self];
        self.beaconStatLabel.text = @"FIRST RUN"; //set the label text
     else 
        self.beaconStatLabel.text = @"REPEAT RUN"; //set the label text
    




    //Looking for and dealing with regions

- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region 
    //looking for a region means looking for a beacon or set of beacons that share a UUID
    [self.locationManager requestStateForRegion:self.beaconRegion];


- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region 
    //if we found a region we start ranging (looking for beaocns)
    [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
    self.regionState.text = @"Region Entered";

    //we'll also test sending a notification
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.alertBody = @"Welcome! Go upstairs, bring beer.";
    notification.soundName = UILocalNotificationDefaultSoundName;
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];


- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region 
    //we have left the region so we'll stop ranging
    [self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
    self.regionState.text = @"Region Exited";

    //we'll also test sending a notification
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.alertBody = @"Thankyou for coming.  For information on our next MeetUp check our MeetUp page.";
    notification.soundName = UILocalNotificationDefaultSoundName;
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];


    //dealing with individual beacons

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region 
    //once beacons are ranged we enter this method

    //we'll grab and log the signal strength of the beacons
    for (int i = 0; i < [beacons count]; i++) 
        CLBeacon *singleBeacon = [[CLBeacon alloc]init];
        singleBeacon = [beacons objectAtIndex:i];
    


    //we get the latest beacon in the array - the closest beacon (strongest signal)
    CLBeacon *beacon = [[CLBeacon alloc] init];
    beacon = [beacons lastObject];

    //update the info labels
    self.uuidLabel.text = beacon.proximityUUID.UUIDString;
    self.majorLabel.text = [NSString stringWithFormat:@"%@", beacon.major];
    self.minorLabel.text = [NSString stringWithFormat:@"%@", beacon.minor];

    //we store some information about that beacon
    NSNumber *beaconMajor = beacon.major;  //it's major (group) number
    NSNumber *beaconMinor = beacon.minor;  //it's minor (individual) number

    //we then call the manageBeacon method and pass through the minor, major, and proximity values
    [self manageBeaconWithMinor:beaconMinor AndMajor:beaconMajor AtRange:beacon.proximity];



- (void)manageBeaconWithMinor:(NSNumber *)minorNumber AndMajor:(NSNumber *)majorNumber AtRange:(CLProximity)proximity 
    //in this method we work out what do do based upon the beacon we are connected to and the range

    //for this test we'll look for the mint beacon and call a view
    if (([minorNumber floatValue] == 59204) && ([majorNumber floatValue] == 33995) && (proximity == CLProximityNear)) 
        //we are going to open up content
        [[tBGlobalStore sharedInstance]setInContentTrue];
        NSLog([[tBGlobalStore sharedInstance] getInContent] ? @"Yes" : @"No");
        //the beacon numbers match the beacon we are expecting so we'll call the next screen
        [self performSegueWithIdentifier:@"mainToContent" sender:self];
    

来自控制台的一些附加信息,您可以看到它正在尝试多次调用视图。

2013-11-22 15:24:56.487 测试信标[670:60b] --- 2013-11-22 15:24:56.489 测试信标[670:60b] ----- 2013-11-22 15:24:56.490 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:56.490 测试信标[670:60b] 0 2013-11-22 15:24:56.491 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:56.492 测试信标[670:60b] -75 2013-11-22 15:24:56.492 测试信标[670:60b] ----- 2013-11-22 15:24:56.493 测试信标[670:60b] --- 2013-11-22 15:24:56.495 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:56.497 测试信标[670:60b] --- 2013-11-22 15:24:56.498 测试信标[670:60b] ----- 2013-11-22 15:24:56.498 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:56.499 测试信标[670:60b] 0 2013-11-22 15:24:56.499 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:56.500 测试信标[670:60b] -75 2013-11-22 15:24:56.500 测试信标[670:60b] ----- 2013-11-22 15:24:56.501 测试信标[670:60b] --- 2013-11-22 15:24:57.487 测试信标[670:60b] --- 2013-11-22 15:24:57.489 测试信标[670:60b] ----- 2013-11-22 15:24:57.489 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:57.490 测试信标[670:60b] 0 2013-11-22 15:24:57.490 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:57.491 测试信标[670:60b] -75 2013-11-22 15:24:57.491 测试信标[670:60b] ----- 2013-11-22 15:24:57.492 测试信标[670:60b] --- 2013-11-22 15:24:57.493 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:57.495 测试信标[670:60b] --- 2013-11-22 15:24:57.495 测试信标[670:60b] ----- 2013-11-22 15:24:57.496 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:57.496 测试信标[670:60b] 0 2013-11-22 15:24:57.497 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:57.497 测试信标[670:60b] -75 2013-11-22 15:24:57.498 测试信标[670:60b] ----- 2013-11-22 15:24:57.499 测试信标[670:60b] --- 2013-11-22 15:24:57.500 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:58.488 测试信标[670:60b] --- 2013-11-22 15:24:58.489 测试信标[670:60b] ----- 2013-11-22 15:24:58.490 测试信标[670:60b] 信标在: 2013-11-22 15:24:58.490 测试信标[670:60b] 0 2013-11-22 15:24:58.491 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:58.491 测试信标[670:60b] -76 2013-11-22 15:24:58.492 测试信标[670:60b] ----- 2013-11-22 15:24:58.492 测试信标[670:60b] --- 2013-11-22 15:24:58.493 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:58.494 测试信标[670:60b] --- 2013-11-22 15:24:58.495 测试信标[670:60b] ----- 2013-11-22 15:24:58.496 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:58.496 测试信标[670:60b] 0 2013-11-22 15:24:58.497 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:58.497 测试信标[670:60b] -76 2013-11-22 15:24:58.498 测试信标[670:60b] ----- 2013-11-22 15:24:58.499 测试信标[670:60b] --- 2013-11-22 15:24:58.500 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:59.488 测试信标[670:60b] --- 2013-11-22 15:24:59.489 测试信标[670:60b] ----- 2013-11-22 15:24:59.489 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:59.490 测试信标[670:60b] 0 2013-11-22 15:24:59.490 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:59.491 测试信标[670:60b] -75 2013-11-22 15:24:59.491 测试信标[670:60b] ----- 2013-11-22 15:24:59.492 测试信标[670:60b] --- 2013-11-22 15:24:59.493 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:24:59.494 测试信标[670:60b] --- 2013-11-22 15:24:59.495 测试信标[670:60b] ----- 2013-11-22 15:24:59.495 testingBeacons[670:60b] 信标在: 2013-11-22 15:24:59.496 测试信标[670:60b] 0 2013-11-22 15:24:59.496 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:24:59.497 测试信标[670:60b] -75 2013-11-22 15:24:59.498 测试信标[670:60b] ----- 2013-11-22 15:24:59.498 测试信标[670:60b] --- 2013-11-22 15:24:59.500 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:25:00.487 测试信标[670:60b] --- 2013-11-22 15:25:00.488 测试信标[670:60b] ----- 2013-11-22 15:25:00.489 testingBeacons[670:60b] 信标在: 2013-11-22 15:25:00.489 测试信标[670:60b] 0 2013-11-22 15:25:00.490 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:25:00.490 测试信标[670:60b] -75 2013-11-22 15:25:00.491 测试信标[670:60b] ----- 2013-11-22 15:25:00.491 测试信标[670:60b] --- 2013-11-22 15:25:00.492 testingBeacons[670:60b] 警告:尝试呈现不在窗口层次结构中的视图! 2013-11-22 15:25:00.493 测试信标[670:60b] --- 2013-11-22 15:25:00.494 测试信标[670:60b] ----- 2013-11-22 15:25:00.494 testingBeacons[670:60b] 信标在: 2013-11-22 15:25:00.495 测试信标[670:60b] 0 2013-11-22 15:25:00.495 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:25:00.496 测试信标[670:60b] -75 2013-11-22 15:25:00.496 测试信标[670:60b] ----- 2013-11-22 15:25:00.497 测试信标[670:60b] --- 2013-11-22 15:25:00.498 testingBeacons[670:60b] 警告:尝试呈现不在窗口层次结构中的视图! 2013-11-22 15:25:01.488 测试信标[670:60b] --- 2013-11-22 15:25:01.489 测试信标[670:60b] ----- 2013-11-22 15:25:01.489 testingBeacons[670:60b] 信标在: 2013-11-22 15:25:01.490 测试信标[670:60b] 0 2013-11-22 15:25:01.490 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:25:01.491 测试信标[670:60b] -72 2013-11-22 15:25:01.492 测试信标[670:60b] ----- 2013-11-22 15:25:01.492 测试信标[670:60b] --- 2013-11-22 15:25:01.493 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图! 2013-11-22 15:25:01.494 测试信标[670:60b] --- 2013-11-22 15:25:01.495 测试信标[670:60b] ----- 2013-11-22 15:25:01.495 testingBeacons[670:60b] 信标在: 2013-11-22 15:25:01.496 测试信标[670:60b] 0 2013-11-22 15:25:01.497 testingBeacons[670:60b] 的 RSSI 为: 2013-11-22 15:25:01.497 测试信标[670:60b] -72 2013-11-22 15:25:01.498 测试信标[670:60b] ----- 2013-11-22 15:25:01.498 测试信标[670:60b] --- 2013-11-22 15:25:01.499 testingBeacons[670:60b] 警告:尝试显示不在窗口层次结构中的视图!

【问题讨论】:

您能否澄清一下“因为测距不会停止”?您的意思是说您基本上会看到“闪烁”,因为当您处于边缘时,Near iBeacon 范围会变得非常跳跃? 我的意思是,即使您靠近信标,在 CLProximityNear 内部,didRangeBeacons 方法也是连续的。在日志中,您可以看到它试图一遍又一遍地加载 UIView。 有趣,所以您对 didEnterRegion 和 didExitRegion 的调用不是“平衡的”? @mrEmpty 哦,我回答了我自己的问题 :) @mrEmpty 不,他们很好。它是持续运行的测距方法,didEnter 和 didExit 在找到或丢失 UUID 时触发一次。 【参考方案1】:

我们遇到了类似的挑战。但是这两种解决方案都有一个问题:Apple 处理测距和接近度的方式存在一个“错误”,你会得到随机切换和 didExitRegion 调用。

看这里:

http://beekn.net/2013/11/ibeacon-tutorial-dealing-with-errors/

您的应用会看似随机调用 didExitRegion,这显然是一个错误,因为位置服务实际上可能会完全关闭一秒钟左右。

我们处理了 viewController 的想法,方法是在 viewController 被推送后停止测距,然后在用户关闭 viewController 后重新启动它。

    之后: [self performSegueWithIdentifier:@"mainToContent" sender:self];

添加:

[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];

    为了在用户关闭 viewController 后重新开始测距,添加:

// 当OutsideViewController被推送时ViewWillAppear重启stopRanging - (void)viewWillAppear:(BOOL)animated [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];

注意:如果你想根据区域变化自动关闭 viewController,你会遇到快速推拉的问题:你会推送一个 viewController,然后它会快速关闭、打开、关闭...... .问题不在于代码,而在于 Apple API 无法始终确定它位于哪个区域。

可能的解决方案? “黑客”可能是添加一个计数器:我们发现,您将手机靠近信标,即使您没有移动手机并且信标信号也没有移动,它也会在接近/立即之间快速切换'实际上并没有改变。这是一个苹果的“错误”!但它只会这样做一两次。

一个选项是添加一个计数器:相当于“只有当您检测到区域更改三到四次时才触发事件”

另一个选项是添加时间:“仅在区域发生变化 4 秒后才触发基于区域变化的事件”

再一次 - 问题不在于您会突然切换(将 VC 推入堆栈或将其从堆栈中拉出),因为您处于“区域之间” - 问题是:

A. Apple API 无法始终正确判断您是直接/近/远,并且会突然改变主意

B. Apple 有时会看似随机关闭定位服务,持续时间不到一秒,迫使您的应用重新进入某个区域并重新开始测距。

【讨论】:

你好@BEEKn - 似乎苹果还有一些工作要做,它是一个相当新且晦涩的 API,所以我并不感到惊讶。我决定创建自己的信标管理器并处理与 NSNotificationCenter 的通信。我会在它工作时发布我的代码。 太棒了 - 您可能还想查看 GitHub 上的 ibeacon-utils。我实际上无法获得 LocationServices 的许可,但它的设置方式有一些好的想法。【参考方案2】:

didRangeBeacons:每个信标每秒调用一次,并且不会为您跟踪附近的变化。

CLLocationManagerDelegate Reference

我建议做的是将您的信标跟踪封装在它自己的类中,该类负责:

    管理信标监控 保留特定 iBeacon 及其当前范围的哈希值。 当特定 iBeacon 的范围发生变化时,调用协议方法通知委托(您的视图控制器)甚至 NSNotificationCenter(您知道这一点是因为您正在维护最近的信标/接近度的哈希值)。李>

【讨论】:

这很有趣,我将研究协议方法。谢谢你。我会暂时保留这个问题,以防其他人想插话。 NSNotificationCenter 似乎是一个简单的解决方案,现在重新构建应用程序。谢谢。

以上是关于从 iBeacon 接近控制视图的主要内容,如果未能解决你的问题,请参考以下文章

从 Appdelegate 设置 iBeacon 操作和监控

iBeacon 监控不适用于 Cordova-plugin-ibeacon

具有接近度和可点击按钮的 IBeacon

信标区域的 iBeacon 监控模式接近度

任何 iBeacon 接近 UUID 的应用回调

嗅探具有接近 UUID 主要次要的 ibeacon 包