Our app consists of Cocos3D and AR. Since upgrading to iOS 8 we had to fix our orientation handling because of the changes Apple brought on us.
However, we still experience some issues that lead me to a rather interesting phenomena (I checked it in our app and in a fresh new project..):
Logging the [UIDevice currentDevice].orientation,[UIApplication sharedApplication].statusBarOrientation and [UIScreen mainScreen].bounds.size shows that when the device is rotated quickly, the device orientation actually changes, however the interface orientation sometimes stays the same and it leads to wrong calculations of the screen bounds... e.g.:
before rotation:
[UIDevice currentDevice].orientation = 1
[UIApplication sharedApplication].statusBarOrientation = 1
[UIScreen mainScreen].bounds.size = 414x736
after first rotation:
[UIDevice currentDevice].orientation = 4
[UIApplication sharedApplication].statusBarOrientation = 4
[UIScreen mainScreen].bounds.size = 736x414
after second rotation:
[UIDevice currentDevice].orientation = 1
[UIApplication sharedApplication].statusBarOrientation = 4
[UIScreen mainScreen].bounds.size = 736x414
Now obviously, after the second rotation, the device is back in portrait mode - well not according to the interface orientation and the screen bounds...
Am I missing something here?
I have had this issue in the past and have concluded that when you set up an observer to the UIDeviceOrientationDidChangeNotification the method you specify is called before all attributes change to reflect the new orientation. The way I dealt with it is to create a function which runs a block of code after a delay and then call:
- (void)deviceOrientationDidChange:(NSNotification *)note
{
RHRunBlockAfterDelay(0.5, ^{
// Execute relevant code...
});
}
The delay function is a wrapper around a call to [self performSelector:#selector(mySel) afterDelay:delay withObject:block]; defined in a separate Functions.h.
Related
On iOS 9.3 iPhones and iPads (actual devices and in the simulator), I am getting inconsistent information (between the device and the simulator) about the [UIScreen mainScreen].bounds after an orientation change notification.
My view controller adds a notification for orientation changes when the view loads:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(deviceOrientationDidChangeNotification:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
The behaviour when the orientation changes is as follows:
- (void) deviceOrientationDidChangeNotification:(NSNotification *) notification {
CGRect frame = [UIScreen mainScreen].bounds;
NSLog(#"Main screen is w=%f h=%f", frame.size.width, frame.size.height);
}
Running this on an iPhone, after the orientation changes, the log message indicates a frame width and height that correspond with the post orientation change screen dimensions.
Running this on an iPad, after the orientation changes, the log message indicates a frame width and height that correspond with the pre orientation change screen dimensions.
Is this kind of inconsistency something that needs to be coded around? I could start trying to detect what the orientation is (landscape or portrait) and then using the width and height values that make sense but that is pretty hacky.
The right way to handle this is to avoid the notification system, instead using the
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator
method on the UIViewController where you want to adapt the UI for an orientation change.
I'm developing an application in iOS8 in landscape mode and everything works fine. I'm using UIKit and don't support portrait mode at all.
Im trying to customise it to iOS7 but the frame that I'm getting is always like the iPhone is in a portrait mode.
I know that the in iOS8 there was a major change with the width and height and it's different in iOS7.
I have the following method which I used to set the window n the appDelegate and when I need the frame at the ViewControllers.
I'm doing the following:
- (CGRect) screenRect {
CGRect screenRect = [UIScreen mainScreen].bounds;
if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
return CGRectMake(0,0,screenRect.size.height, screenRect.size.width);
}
return screenRect;
}
The views look fine but the left/right side of the screen (depends on the device orientation) behaves like it's outside the frame. i.e doesn't response to gestures!
When I'm setting the fame to the new "screenRect" it keeps turning back to the device bounds.
Thank you very much.
I have an app that supports orientation changes and rotates accordingly. I listen to the event UIApplicationWillChangeStatusBarOrientationNotification. Now during the load of this app I add some views and set them up in my app, based on the current orientation.
in iOS 6, this works fine and the app responds and rotates properly, so the user can load in both landscape and portrait mode and code works fine.
in iOS 5, if I load the app in portrait mode, the app works fine, and once that load has been completed in portrait mode, and the UI is aligned and sized, it will respond to other orientation changes to landascape or portrait. The problem I have is this : When loading iOS 5 in landscape mode, and while physically laying the device with iOS 5 on a flat surface to ensure its landscape, I get a OrientationNotification that moves from Landscape to portrait ( although the device didn't change ).
So another device iOS 6 in the same experiment, loads properly and I don't get any weird events of rotation changes that didn't occur, but with iOS 5 I do get them!!
Any ideas?
I am supporting the orientation for both iOS's
- (BOOL)shouldAutorotate {
return YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
It sounds like iOS 5 believes things should be portrait if, in fact, there is no physical orientation (i.e. flat up or down) and iOS 6 doesn't. For what it might be worth, to determine the orientation to display stuff when it matters, I use the actual device orientation when available and the status bar orientation when the device is flat. For instance:
// Get a useful screen orientation.
// If the device is physically in portrait or landscape then
// that is the orientation.
// If it is not, then it is probably flat on a table and use
// the orientation of the status bar which should be set to
// the last physical orientation.
//
// screen Orientation
//------------------------------------------------------------
+ (UIDeviceOrientation) screenOrientation {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation!=UIInterfaceOrientationPortrait
&& orientation!=UIInterfaceOrientationPortraitUpsideDown
&& orientation!=UIInterfaceOrientationLandscapeLeft
&& orientation != UIInterfaceOrientationLandscapeRight) {
// Not known at this time. Use the status bar orientation
orientation = [[UIApplication sharedApplication] statusBarOrientation];
}
return orientation;
}
I'm not sure that helps you directly, but perhaps in your notification handler you could check to see what the actual status bar orientation is. Or maybe the timing of the notification and the status-bar orientation change doesn't make that work.
I created a UIViewController (based on How to switch views when rotating) to switch between 2 views when the device rotates. Each view is "specialized" for a particular orientation.
It uses the UIDeviceOrientationDidChangeNotification notification to switch views:
-(void) deviceDidRotate: (NSNotification *) aNotification{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
NSLog(#"Device rotated to %d!", orientation);
if ((orientation == UIDeviceOrientationPortrait) ||
(orientation == UIDeviceOrientationPortraitUpsideDown)) {
[self displayView:self.portraitViewController.view];
}else if ((orientation == UIDeviceOrientationLandscapeLeft) ||
(orientation == UIDeviceOrientationLandscapeRight)) {
[self displayView:self.landscapeViewController.view];
}
}
and sort of works. The problems shows up when I rotate to Landscape and then back to Portrait. When going back to portrait the subviews aren't displayed in the right place, specially the UIPickerView:
First Time Portrait:
Rotate to Landscape:
Back to Portrait:
If I repeat the rotation process, things just get worse. What am I doing wrong?
The source code is here: http://dl.dropbox.com/u/3978473/forums/Rotator.zip
Thanks in advance!
To solve your offset problems, rewrite the displayView: method as below.
-(void) displayView: (UIView *)aView{
self.view = aView;
}
Rotations however are strange. you should review that part of code.
Use the UIViewController rotation methods
(void)willRotateToInterfaceOrientation:duration:
(void)didRotateFromInterfaceOrientation:
instead of -(void)deviceDidRotate:
Much simpler, you will avoid that strange bouncing, and you don't need notifications any more.
Do some reading on the apple documentation on the methods i specified above.
Hope this helps.
OK, I found the error. It's pretty simple and stupid: I mixed frame and bounds.
In the displayView: code I was setting the frame of the child view to the frame of the parent view, when it should be the bounds of the parent.
I use this code:
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
to get iPad orientation but it returns UIDeviceOrientationUnknown when I use OpenGL rendering. When I play movies or I display UIViews it return the correct value. Do you know how may I get correct orientation? I don't want to use the accelerometer because I want to know when the device has locked orientation.
Thank you!
There are situations where the accelerometer can’t get an accurate read on the orientation, such as when the device is held flat. You should still be able to get the current orientation using:
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
In iOS 13, the statusBarOrientation has been deprecated. The new preferred way of getting this orientation is
let orientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
If I add [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; the orientation will be correct even in OpenGL rendering mode. UIDeviceOrientationUnknown orientation is sometimes returned but only for short time.