Detecting iOS orientation change instantly - iphone

I have a game in which the orientation of the device affects the state of the game. The user must quickly switch between Landscape, Portrait, and Reverse Landscape orientations. So far I've been registering the game for orientation notifications via:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
But it is far too slow - there seems to be about a second delay between rotating the phone and the notification actually being fired. I need a way to INSTANTLY detect changes in the device's orientation. I have tried experimenting with the gyroscope, but am not yet familiar enough with it to know whether or not it is the solution I am looking for.

Add a notifier in the viewWillAppear function
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
The orientation change notifies this function
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
which in-turn calls this function where the moviePlayerController frame is orientation is handled
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
switch (orientation)
{
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
{
//load the portrait view
}
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
{
//load the landscape view
}
break;
case UIInterfaceOrientationUnknown:break;
}
}
in viewDidDisappear remove the notification
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
I guess this is the fastest u can have changed the view as per orientation

That delay you're talking about is actually a filter to prevent false (unwanted) orientation change notifications.
For instant recognition of device orientation change you're just gonna have to monitor the accelerometer yourself.
Accelerometer measures acceleration (gravity included) in all 3 axes so you shouldn't have any problems in figuring out the actual orientation.
Some code to start working with accelerometer can be found here:
How to make an iPhone App – Part 5: The Accelerometer
And this nice blog covers the math part:
Using the Accelerometer

Why you didn`t use
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
?
Or you can use this
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Or this
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
Hope it owl be useful )

For my case handling UIDeviceOrientationDidChangeNotification was not good solution as it is called more frequent and UIDeviceOrientation is not always equal to UIInterfaceOrientation because of (FaceDown, FaceUp).
I handle it using UIApplicationDidChangeStatusBarOrientationNotification:
//To add the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didChangeOrientation:)
//to remove the
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
...
- (void)didChangeOrientation:(NSNotification *)notification
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(orientation)) {
NSLog(#"Landscape");
}
else {
NSLog(#"Portrait");
}
}

Try making your changes in:
- (void) viewWillLayoutSubviews {}
The code will run at every orientation change as the subviews get laid out again.

#vimal answer did not provide solution for me. It seems the orientation is not the current orientation, but from previous orientation. To fix it, I use [[UIDevice currentDevice] orientation]
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIDevice currentDevice] orientation]];
}
Then
- (void) adjustViewsForOrientation:(UIDeviceOrientation) orientation { ... }
With this code I get the current orientation position.

Related

Turn off default rotate animation in iOS

When I rotate my iDevice form portrait to landscape, the screen rotates fine, but I'm seeing black borders moving with it, so it looks more 'real'. I find it embarrassing to see in iOS 7 and many apps have thrashed this behaviour (like Instagram).
What I want to do is hide those black borders that look totally unnecessary when rotating a device. How do I disable this standard animation?
In the parent view controller viewdidload method add this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
Then add this method
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (UIInterfaceOrientationIsLandscape(orientation) && !self.modalViewController) {
[self presentModalViewController:carouselView animated:YES];
[Globals sharedGlobals].startedAtLandscape = YES;
}
if (UIInterfaceOrientationIsPortrait(orientation) && self.modalViewController) {
[self dismissModalViewControllerAnimated:YES];
[Globals sharedGlobals].startedAtLandscape = NO;
}
}
Then to prevent animation:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
return NO;
}
return YES;
}
I found the best solution is turn animation off before rotation, and turn it back after rotation.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
...
[UIView setAnimationsEnabled:NO];
}
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
...
[UIView setAnimationsEnabled:YES];
}

Camera App Supported Orientation?

I want to do the same as Camera app: it looks like only Portrait mode is supported but when you rotate certain views get rotated while toolbar and some controls are in the same place.
I tried to disable all supported orientation in PLIST file but this calls no delegate method such as shouldRotate, or, obviously, willRotate to perform orientation change.
When supported orientation are in PLIST, iPhone rotates all views, even though I return NO for shouldRotate method.
How to solve such dilemma?
In the info.plist, set the only supported orientation to Portrait. Then, in you viewDidLoad method, add:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
This will call the method:
- (void)didRotate:(NSNotification *)aNotification; {
UIDeviceOrientation currentDeviceOrientation = [[UIDevice currentDevice] orientation];
switch(currentDeviceOrientation){
case UIDeviceOrientationPortrait:
break;
case UIDeviceOrientationPortraitUpsideDown:
break;
case UIDeviceOrientationLandscapeLeft:
break;
case UIDeviceOrientationLandscapeRight:
break;
default:
break;
}
}
From there, you can do whatever you want based on any option. Also, don't forget to add this:
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
To you dealloc method. Hope that Helps!

populate entire landscape screen with new uiview upon device orientation change from portrait to landscape

So my iPhone application currently has a tabviewcontroller that populates the entire screen. The app only runs in portrait mode. My task has been to detect device orientation changes, and once it changes to landscape, have a new uiview populate the entire screen.
I already have the device orientation change detection working. I've used an NSNotificationCenter to successfully call a helper method, deviceOrientationChanged, once an orientation change is detected. IF the change was to landscape mode, I run a certain block of code.
In this block of code I have already tried various things, none of which are successful. Simply saying self.view = newViewThing; does not work because the statusbar is still present at the top and the tabs are still present at the bottom.
I have also tried adding this newViewThing as a subview to the UIWindow. This did not work because while the view was added, it was not oriented correctly.
THE QUESTION IS: is there a way to load an entirely new uiview once a device orientation change is detected? Thank you in advance.
Yes, there is a way to load a new view. I make it in my app that way:
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
- (void)updateLandscapeView
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.landscapeView animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
And also I have added this code to viewDidLoad:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
and this code to dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

BAD_ACCESS while landscape orientation

I want to keep my view landscape. For that i am using this code but BAD_ACCESS is coming.
Here I am writing this code for camera overlayView.
-(void)viewDidLoad
{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];
//Set Notifications so that when user rotates phone, the orientation is reset to landscape.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
//Refer to the method didRotate:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification" object:nil];
[super viewDidLoad];
}
- (void) didRotate:(NSNotification *)notification
{
//Maintain the camera in Landscape orientation
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];
}
Why it is giving BAD ACCESS ?
To keep your view landscape, just return NO in your shouldAutorotateToInterfaceOrientation: method for portrait orientations, like tihs :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

iPhone center activity indicator regardless of screen orientation

How can I center an activity indicator programmatically regardless of screen orientation?
Try setting the center property of your activity view, like this:
activity.center = CGPointMake(self.view.frame.size.width/2,self.view.frame.size.height/2);
In viewDidLoad, register for notifications for the device rotation:
- (void)viewDidLoad {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification object:nil];
}
and implement didRotate:
-(void)didRotate:(NSNotification *)notification {
if (activity) {
activity.center = CGPointMake(self.view.frame.size.width/2,self.view.frame.size.height/2);
}
}
I suggest you use https://github.com/jdg/MBProgressHUD
Which is a great library for doing all kinds of "Loading" screens.