2
votes

I have been playing around with iBeacons for a few weeks now and was woundering if it was possible to monitor two beacons from the same view controller.

For example so far I have made an app with three sperate views that each react to a seperate beacon changing content dependent on range and triggering dynamic content like music and video. All these view are the same layout so I was woundering if I could change it so I had one view controller that changed the content dependent on what beacon I am near rather than having to change the view.

This would make more sense as you can only monitor one which means when the segue triggers and the new beacon is being monitored if you move back towards the first beacon it doesn't chnage back to that view when you enter the first beacons proximity.

I'm sure there is a way to do this as beacons are being used in apple store in america to trigger promotion, etc and they changhe dependednt on the closest beacon with no problem.

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface ExhibitsViewController : UIViewController <CLLocationManagerDelegate,UIScrollViewDelegate>
{
CLBeacon *beacon;
}

@property (strong, nonatomic) CLBeaconRegion *beaconRegion;
@property (strong, nonatomic) CLLocationManager *locationManager

@end

This is the code in my header file that is defining the beacon, beaconRegion and locationManager.

#import "MultipleBeaconsViewController.h"

@interface MultipleBeaconsViewController ()  
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *rangeLabel;
@property (weak, nonatomic) IBOutlet UILabel *uuidLabel;
@end

@implementation MultipleBeaconsViewController

NSUUID *iBeacon1uuid;
NSUUID *iBeacon2uuid;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
    // Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.

self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self initRegion];

[self locationManager:self.locationManager didStartMonitoringForRegion:self.iBeacon1Region];
[self locationManager:self.locationManager didStartMonitoringForRegion:self.iBeacon2Region];

}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (void)initRegion {

iBeacon1uuid = [[NSUUID alloc] initWithUUIDString:@"11111111-2222-3333-4444-555555555555"];
_iBeacon1Region = [[CLBeaconRegion alloc] initWithProximityUUID:iBeacon1uuid identifier:@"com.private.Gallery"];
_iBeacon1Region.notifyOnEntry = YES;
_iBeacon1Region.notifyOnExit = YES;

// launch app when display is turned on and inside region
_iBeacon1Region.notifyEntryStateOnDisplay = YES;

if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
{
    [_locationManager startMonitoringForRegion:_iBeacon1Region];
    [_locationManager startRangingBeaconsInRegion:_iBeacon1Region];
}

iBeacon2uuid = [[NSUUID alloc] initWithUUIDString:@"55555555-4444-3333-2222-111111111111"];
_iBeacon2Region = [[CLBeaconRegion alloc] initWithProximityUUID:iBeacon2uuid identifier:@"com.private.Gallery"];
_iBeacon2Region.notifyOnEntry = YES;
_iBeacon2Region.notifyOnExit = YES;

// launch app when display is turned on and inside region
_iBeacon2Region.notifyEntryStateOnDisplay = YES;

if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
{
    [_locationManager startMonitoringForRegion:_iBeacon2Region];
    [_locationManager startRangingBeaconsInRegion:_iBeacon2Region];
}
}

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {

if ([region isKindOfClass:[CLBeaconRegion class]])
{

    CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;

    if ([beaconRegion.proximityUUID isEqual: iBeacon1uuid])
    {
        _titleLabel.text = @"Beacon 1 proximity entered";

    }
    else if ([beaconRegion.proximityUUID isEqual: iBeacon2uuid])
    {
        _titleLabel.text = @"Beacon 2 proximity entered";
    }

}

}

-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{

if ([region isKindOfClass:[CLBeaconRegion class]]) {

    CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;


    if ([beaconRegion.proximityUUID isEqual: iBeacon1uuid])
    {

        _titleLabel.text = @"Beacon 1 proximity exited";
    }
    else if ([beaconRegion.proximityUUID isEqual: iBeacon2uuid])
    {

        _titleLabel.text = @"Beacon 2 proximity exited";
    }

}
}

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

if ([region isKindOfClass:[CLBeaconRegion class]]) {

    for (CLBeacon *beacon in beacons)
    {

        NSString *beaconID = [beacons objectAtIndex:0];
        NSLog(@"%@",beaconID);

        if ([region.proximityUUID isEqual:iBeacon1uuid])
        {

            if (beacon.accuracy >=0.000001 && beacon.accuracy <=0.500000)
            {
            _titleLabel.text = @"Beacon 1";
            _rangeLabel.text = [NSString stringWithFormat:@"%f",beacon.accuracy];
            _uuidLabel.text = [NSString stringWithFormat:@"%@", beacon.proximityUUID];
            }
        }
        else if ([region.proximityUUID isEqual:iBeacon2uuid])
        {
            if (beacon.accuracy >=0.000001 && beacon.accuracy <=0.500000) {
                _titleLabel.text = @"Beacon 2";
                _rangeLabel.text = [NSString stringWithFormat:@"%f",beacon.accuracy];
                _uuidLabel.text = [NSString stringWithFormat:@"%@", beacon.proximityUUID];
            }
            else
            {

            }
        }

    }
}
}

This is my code in the implementation file that is ranging the beacon.

3

3 Answers

4
votes

Yes you can. To do that you don't have to keep property self.beaconRegion you just create as many as you like and start monitor and start ranging for it, for example this is how I do that:

//Reion 1

CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:major minor:minor identifier:identifier];
region.notifyOnEntry = YES;
region.notifyOnExit = YES;
// launch app when display is turned on and inside region
region.notifyEntryStateOnDisplay = YES;

if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
{
    [_locationManager startMonitoringForRegion:region];
    [_locationManager startRangingBeaconsInRegion:region];
}

//Region 2

   CLBeaconRegion *region2 = [[CLBeaconRegion alloc] initWithProximityUUID:uuid2 major:major2 minor:minor2 identifier:identifier2];
    region2.notifyOnEntry = YES;
    region2.notifyOnExit = YES;
    // launch app when display is turned on and inside region
    region2.notifyEntryStateOnDisplay = YES;

    if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
    {
        [_locationManager startMonitoringForRegion:region2];
        [_locationManager startRangingBeaconsInRegion:region2];
    }

// and so on

After that in every self.locationManager delegate method make sure which beacon you have, for example:

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {

    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        CLBeaconRegion *beaconRegion = (CLBeaconRegion *)region;

        // Find out which beacon you have,
        // I check just UUID but maybe your beacons have the same uuid but major, minor are difference so you need to check minor/major as well
        if ([beaconRegion.proximityUUID isEqual:UUIDFIRST]) {

            //Do some stuff
        }
    }
}

If you want to see which beacon are nearest you use:

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

enumerate beacons array:

if ([region isKindOfClass:[CLBeaconRegion class]]) {

        for (CLBeacon *beacon in beacons) { // 1
            // do the checking which beacon you have
            if ([beaconRegion.proximityUUID isEqual:UUIDFIRST]) { // 2

            }

                 // check beacon.accuracy property this should gives you distance in meters meters
                 //beacon.accuracy // 3
            }
        }
    }

// Extended

If you are in range two (or more) beacons the beacons are passed to you in beacons array. By calling [beacons lastObject] you get just one beacon. What you have to do is enumerate (go throughout all of the objects in array) see no 1 in my code above (I edited it a bit) and see what beacon you have (2) after that you compare the distance (3) and the one with less value is the nearest one.

3
votes

A UUID can be reused on many iBeacons. Each beacon has a major and a minor number to differentiate each one.

So a company would buy a single UUID, but (taking a store),

All majors = 1 are on the first floor:

  • minor 1 = to womens clothes
  • minor 2 = mens clothes

Major 2 = second floor:

  • minor 1 = kids clothes
  • minor 2 = footwear

... and so on. As the app walks around the store the beacons can tell it where they are and offer ad's / videos depending on the data.

You check for these values inside your didRangeBeacons callback. I also believe an app can monitor up to 20 regions at once, so the region attribute inside that callback would contain the different UUID

0
votes

The identifier provided in

[[CLBeaconRegion alloc] initWithProximityUUID:iBeacon1uuid identifier:@"com.private.Gallery"]; 

has to be different for each beacon you are monitoring.

In your case, they are both @"com.private.Gallery"