了解 iOS 中的 iBeacons:didDetermineState 和 didEnterRegion 事件

Posted

技术标签:

【中文标题】了解 iOS 中的 iBeacons:didDetermineState 和 didEnterRegion 事件【英文标题】:Understanding iBeacons in iOS: didDetermineState and didEnterRegion events 【发布时间】:2015-10-08 08:41:00 【问题描述】:

第一部分:

我编写了以下代码来监控 iBeacons。我想检测 didEnterRegiondidExitRegion 事件。然而它永远不会发生。您能否查看代码并提出可能缺少的内容?

我使用 Apple AirLocate sample code 将一台设备配置为 iBeacon 并执行以下步骤来测试我的代码:

步骤:

    在设备B上编译并执行AirLocate示例代码 在设备 A 上编译并执行此代码 在设备 B 中使用 AirLocate 应用程序将设备配置为 iBeacon,选择以下 UUID:“74278BDA-B644-4520-8F0C-720EAF059935”

结果:

声明内部消息

预期结果:

声明内部消息 进入地区

这是为什么呢?

这些是我的 plist 条目:

代码:

#import "BeaconMonitoring.h"

@implementation BeaconMonitoring

- (instancetype)init

    self = [super init];
    if (self) 
        self.locationManager = [[CLLocationManager alloc] init];

        if([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) 
            [self.locationManager requestAlwaysAuthorization];
        

        self.locationManager.delegate = self;
        self.locationManager.pausesLocationUpdatesAutomatically = NO;

        self.monitoredRegions = [[NSMutableArray alloc] initWithCapacity:10];
    
    return self;


- (void) startRangingForBeacons
    NSLog(@"in startRangingForBeacons");
    [self.locationManager startUpdatingLocation];
    [self startMonitoringForRegion:[[NSUUID alloc] initWithUUIDString:@"74278BDA-B644-4520-8F0C-720EAF059935"] :@"b"];


- (void) startMonitoringForRegion:(NSUUID*)beaconUUID :(NSString*)regionIdentifier
   /**
    Alternatively:
    CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:@"xxxx"
    major:10
    minor:20
    identifier:@"name"]
    **/

    // Override point for customization after application launch.
    CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:regionIdentifier];

    beaconRegion.notifyEntryStateOnDisplay = NO;
    beaconRegion.notifyOnEntry = YES;
    beaconRegion.notifyOnExit  = YES;

    [self.locationManager startMonitoringForRegion:beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];
    [self.monitoredRegions addObject:beaconRegion];


- (void) stopRangingForbeacons
    NSLog(@"in stopRangingForbeacons");
    [self.locationManager stopUpdatingLocation];

    for (int i=0; i < [self.monitoredRegions count]; i++) 
        NSObject * object = [self.monitoredRegions objectAtIndex:i];

        if ([object isKindOfClass:[CLBeaconRegion class]]) 
            CLBeaconRegion * region = (CLBeaconRegion*)object;
            [self.locationManager stopMonitoringForRegion:region];
            [self.locationManager stopRangingBeaconsInRegion:region];
        
        else
            NSLog(@"Serious error, should never happen!");
        
    



#pragma CLLocationManagerDelegate

-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region 
    [manager startRangingBeaconsInRegion:(CLBeaconRegion*)region];
    [self.locationManager startUpdatingLocation];
    NSLog(@"You entered the region.");


-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region 
    [manager stopRangingBeaconsInRegion:(CLBeaconRegion*)region];
    [self.locationManager stopUpdatingLocation];


    NSDictionary * notificationData = @ @"value" : @"exitedRegion";
    [[NSNotificationCenter defaultCenter] postNotificationName:@"dataUpdate" object:nil userInfo:notificationData];

    NSLog(@"You exited the region.");
    // [self sendLocalNotificationWithMessage:@"You exited the region."];


- (void) locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region

    NSLog(@"did determine state");

    switch (state) 
        case CLRegionStateInside:
            NSLog(@"state inside");
            break;
        case CLRegionStateOutside:
            NSLog(@"state outside");
            break;
        case CLRegionStateUnknown:
            NSLog(@"state unknown");
            break;
        default:
            NSLog(@"Default case: Region unknown");
            break;
    


-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region 

    NSLog(@"Did range %lu beacon in region %@", (unsigned long)[beacons count], region.identifier);

    NSString * visibleInformation = [NSString stringWithFormat:@"(%lu)", (unsigned long)[beacons count]];

    for (int i=0; i<[beacons count]; i++) 
        CLBeacon *beacon = [beacons objectAtIndex:i];

        if ([beacons count] == 1) 
            NSNumber * distance =  [NSNumber numberWithFloat:beacon.accuracy];
            visibleInformation = [NSString stringWithFormat:@"%i-%i is %f", beacon.major.intValue, beacon.minor.intValue, distance.doubleValue];
        
        else
            visibleInformation = [visibleInformation stringByAppendingString:[NSString stringWithFormat:@" %i-%i ", beacon.major.intValue, beacon.minor.intValue]];
        
    


@end

第二部分:

我查看了 AirLocate 源代码,以了解是否必须在状态内部消息中触发某些内容才能使监控正常工作。但是我发现** didDetermineState** 方法是在AppDelegate 中实现的。

这是为什么呢?

- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region

    /*
     A user can transition in or out of a region while the application is not running. When this happens CoreLocation will launch the application momentarily, call this delegate method and we will let the user know via a local notification.
     */
    UILocalNotification *notification = [[UILocalNotification alloc] init];

    if(state == CLRegionStateInside)
    
        notification.alertBody = NSLocalizedString(@"You're inside the region", @"");
    
    else if(state == CLRegionStateOutside)
    
        notification.alertBody = NSLocalizedString(@"You're outside the region", @"");
    
    else
    
        return;
    

    /*
     If the application is in the foreground, it will get a callback to application:didReceiveLocalNotification:.
     If it's not, ios will display the notification to the user.
     */
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

【问题讨论】:

您是否添加了 plist 条目以获得获取用户位置的权限? @jerzo 我添加了 plist 入口文件截图。第一个条目(NSLocationAlwaysUsageDescription)是我使用的,但我不确定它是否足够。 是的,这就够了,你在哪里调用了你的方法'startRangingForBeacons'?我没有找到对该方法的任何调用 【参考方案1】:

假设:R = B 制作并由 A 收听的区域

案例一

如果 A 在 B 之前启动:

    应用 A 应该告诉您确定区域 R = 外部的状态 应用 A 应该运行 didEnter Region R

案例2

如果 A 实际上是在 B 之后开始的:

    它应该只运行determineState Region R = inside

结束。这里没有 2 因为它从不进入范围。它是在里面开始的

【讨论】:

非常感谢您的明确答复。如果我禁用 B 会发生什么?我期待在外面得到一个 didExitRegion 事件和状态,但我也没有得到。 PS:另外,Apple 为什么要在 AppDelegate 中为 AirLocate 实现 didDetermineState?有什么特别的原因吗?我本来希望在每次状态更改时多次调用此方法。 didDetermineState 仅在您开始监控时自动调用 - 否则,您必须调用 [manager determineState:R] 关闭 BT 时不会调用didExit .. IIRC .. 可能是位置管理器的一些错误委托?

以上是关于了解 iOS 中的 iBeacons:didDetermineState 和 didEnterRegion 事件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS、Swift 中搜索 iBeacons UUID,并扫描周围的所有 iBeacons

核心位置、iBeacon 和区域监控

iOS 中的 iBeacons - 没有来自实际 BLE 设备的回调,但是当另一个 iOS 设备用作广播器时会调用 didRangeBeacons

在后台使用 iBeacon 或 CoreBluetooth 识别 iOS 设备

iBeacon、蓝牙 BLE 和车队管理

如何查找 iBeacon 范围内的设备数量?