i m working on estimote beacon.
I try to present a ViewController when i m in switch Case "immediate". But when i the view is loaded, i have a warning :
2014-03-13 02:44:26.017 ProximityDemo[856:60b] Warning: Attempt to present on whose view is not in the window hierarchy!
Why ? I think the presentView Method is still working while i m on the new view.
Also when i m on the new View , i would like to pop to old view when i m in Case "Near"
I Think i have to implement all the the code in the new ViewController ? (presentProductViewController) Is there a way to have all the proximity/distance controll in only one Controller ?
Here is my code :
ESTViewController :
#import "ESTViewController.h"
#import <ESTBeaconManager.h>
#import "PresentProductViewController.h"
@interface ESTViewController () <ESTBeaconManagerDelegate>
@property (nonatomic, strong) ESTBeaconManager* beaconManager;
@property (nonatomic, strong) ESTBeacon* selectedBeacon;
@end
@implementation ESTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
/////////////////////////////////////////////////////////////
// setup Estimote beacon manager
// craete manager instance
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
self.beaconManager.avoidUnknownStateBeacons = YES;
// create sample region object (you can additionaly pass major / minor values)
ESTBeaconRegion* region = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_IOSBEACON_PROXIMITY_UUID
identifier:@"EstimoteSampleRegion"];
// start looking for estimote beacons in region
// when beacon ranged beaconManager:didRangeBeacons:inRegion: invoked
[self.beaconManager startRangingBeaconsInRegion:region];
}
-(void)beaconManager:(ESTBeaconManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(ESTBeaconRegion *)region
{
if([beacons count] > 0)
{
if(!self.selectedBeacon)
{
// initialy pick closest beacon
self.selectedBeacon = [beacons objectAtIndex:0];
}
else
{
for (ESTBeacon* cBeacon in beacons)
{
// update beacon it same as selected initially
if([self.selectedBeacon.major unsignedShortValue] == [cBeacon.major unsignedShortValue] &&
[self.selectedBeacon.minor unsignedShortValue] == [cBeacon.minor unsignedShortValue])
{
self.selectedBeacon = cBeacon;
}
}
}
// beacon array is sorted based on distance
// closest beacon is the first one
NSString* labelText = [NSString stringWithFormat:
@"Major: %i, Minor: %i\nRegion: ",
[self.selectedBeacon.major unsignedShortValue],
[self.selectedBeacon.minor unsignedShortValue]];
// calculate and set new y position
switch (self.selectedBeacon.proximity)
{
case CLProximityUnknown:
{
labelText = [labelText stringByAppendingString: @"Unknown"];
break;
}
case CLProximityImmediate:
{
labelText = [labelText stringByAppendingString: @"Immediate"];
PresentProductViewController *showViewController = [[PresentProductViewController alloc] initWithNibName:@"PresentProductViewController" bundle:nil];
[self presentViewController:showViewController animated:YES completion:nil];
break;
}
case CLProximityNear:
{
labelText = [labelText stringByAppendingString: @"Near"];
break;
//[self.navigationController popToRootViewControllerAnimated:YES];
//ESTViewController *initViewController = [[ESTViewController alloc]init];
//[self presentViewController:initViewController animated:YES completion:nil];
}
case CLProximityFar:
{
labelText = [labelText stringByAppendingString: @"Far"];
break;
}
default:
break;
}
self.distanceLabel.text = labelText;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
PresentProductViewController :
#import "PresentProductViewController.h"
#import <ESTBeaconManager.h>
#import "ESTViewController.h"
@interface PresentProductViewController () <ESTBeaconManagerDelegate>
@property (nonatomic, strong) ESTBeaconManager* beaconManager;
@property (nonatomic, strong) ESTBeacon* selectedBeacon;
@end
@implementation PresentProductViewController
- (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 from its nib.
[self.activityIndicator startAnimating];
/////////////////////////////////////////////////////////////
// setup Estimote beacon manager
// craete manager instance
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
self.beaconManager.avoidUnknownStateBeacons = YES;
// create sample region object (you can additionaly pass major / minor values)
ESTBeaconRegion* region = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_IOSBEACON_PROXIMITY_UUID
identifier:@"EstimoteSampleRegion"];
// start looking for estimote beacons in region
// when beacon ranged beaconManager:didRangeBeacons:inRegion: invoked
[self.beaconManager startRangingBeaconsInRegion:region];
}
-(void)beaconManager:(ESTBeaconManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(ESTBeaconRegion *)region
{
if([beacons count] > 0)
{
if(!self.selectedBeacon)
{
// initialy pick closest beacon
self.selectedBeacon = [beacons objectAtIndex:0];
}
else
{
for (ESTBeacon* cBeacon in beacons)
{
// update beacon it same as selected initially
if([self.selectedBeacon.major unsignedShortValue] == [cBeacon.major unsignedShortValue] &&
[self.selectedBeacon.minor unsignedShortValue] == [cBeacon.minor unsignedShortValue])
{
self.selectedBeacon = cBeacon;
}
}
}
// beacon array is sorted based on distance
// closest beacon is the first one
self.labelText.text = [NSString stringWithFormat:
@"Major: %i, Minor: %i\nRegion: ",
[self.selectedBeacon.major unsignedShortValue],
[self.selectedBeacon.minor unsignedShortValue]];
// calculate and set new y position
switch (self.selectedBeacon.proximity)
{
case CLProximityUnknown:
{
self.labelText.text = [self.labelText.text stringByAppendingString: @"Unknown"];
break;
}
case CLProximityImmediate:
{
self.labelText.text = [self.labelText.text stringByAppendingString: @"Immediate"];
break;
}
case CLProximityNear:
{
self.labelText.text = [self.labelText.text stringByAppendingString: @"Near"];
break;
//UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main.storyboard" bundle:nil];
//UIViewController *initViewController = [storyBoard instantiateInitialViewController];
//[self.navigationController pushViewController:initViewController animated:YES];
//ESTViewController *initViewController = [[ESTViewController alloc]init];
//[self presentViewController:initViewController animated:YES completion:nil];
//[self.navigationController popToRootViewControllerAnimated:YES];
}
case CLProximityFar:
{
self.labelText.text = [self.labelText.text stringByAppendingString: @"Far"];
break;
}
default:
break;
}
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[self.activityIndicator stopAnimating];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
I m sure i m doing wrong. Thanks for your help.
The ESTBeaconManager
is probably continuously delivering the latest changes via didRangeBeacons:inRegion
even when you have presented PresentProductViewController
. I think the following is what happens:
ESTViewController
's didRangeBeacons:inRegion
is called with a non-empty array of beacons.proximity
property of the selected beacons is CLProximityImmediate
, and PresentProductViewController
is presented modally.ESTViewController
's didRangeBeacons:inRegion
is called with new changes. Again, a beacon has proximity immediate, and a new instance of PresentProductViewController
is presented modally.From ViewController Programming Guide
Any view controller object can present a single view controller at a time.
If the assumption outlined above is correct, you are trying to present multiple viewcontrollers modally at the same time, which is not a valid action. You could try to stop ranging for beacons when the view disappears, and then start ranging again when it appears. This will prevent the beacon manager from notifying ESTViewController
of changes while it is not in a position to present viewcontrollers. Override viewWillDisappear
and viewWillAppear
methods to achieve this:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.beaconManager stopRangingBeaconsInRegion:region];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.beaconManager startRangingBeaconsInRegion:region];
}