UIInterfaceOrientation bug? - iphone

In my iPhone app I need to detect the current orientation and I have to determine if I'm in portrait or landscape. I use this code:
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"portrait");
...
} else {
NSLog(#"landscape");
...
}
Everything is ok when my iPhone is in my hand.
But when i put it on the table and i run the application, the content is displayed on the screen in portrait mode and my code goes to else and NSLog prints landscape.
Is my test incomplete ? How to prevent this case ?
EDIT : the test is performed in my controller viewDidLoad method and my application handle rotation.

UIDevice.orientation is of type UIDeviceOrientation, which is a superset of UIInterfaceOrientation. You are probably getting the value UIDeviceOrientationFaceUp.
This underscores that yes, your test is incomplete. You should write something like this:
UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
NSLog(#"portrait");
...
} else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
NSLog(#"landscape");
...
} else {
NSLog(#"WTF? %d", orientation);
assert(false);
}
Then, you'll know if you if you've missed something.

UIDevice.orientation can return that the device is flat or upside down (not inverted portrait, upside-down as in laying on its face). Instead call UIViewController.interfaceOrientation on your root view controller.

I would recommend using UIDeviceOrientationIsValidInterfaceOrientation(orientation)
It will tell you if its a valid orientation (valid being either landscape or portrait, not FaceUp/FaceDown/UnKnown). Then you can treat it as if its portrait if its unknown.
This is how I do it:
if (UIDeviceOrientationIsValidInterfaceOrientation(interfaceOrientation) && UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
// handle landscape
} else {
// handle portrait
}

Related

how to know the device orientation correctly

I want to know the device orientation when user start my app , in order to produce different view. What I find strange is as below:
- (void)viewDidLoad
{
if ([UIDevice currentDevice].orientation == UIDeviceOrientationPortrait) {
NSLog(#"1");
}
if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) {
NSLog(#"2");
}
if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) {
NSLog(#"3");
}
if ([UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown) {
NSLog(#"4");
}
}
No one is printed! I did it on the ipad simulator and I think the orientation should be UIDeviceOrientationPortrait. Why this happen? and how to know the orientation correctly?
Try to use
[UIApplication sharedApplication].statusBarOrientation
instead of
[UIDevice currentDevice].orientation
It may be the case that the simulator simply cannot be in an orientation state (it makes no sense, as you can't really rotate your computer...) Have you checked whether it returns UIDeviceOrientationUnknown?
The documentation or UIDevice states:
You get the current orientation using the orientation property or
receive change notifications by registering for the
UIDeviceOrientationDidChangeNotification notification. Before using
either of these techniques to get orientation data, you must enable
data delivery using the beginGeneratingDeviceOrientationNotifications
method
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIDevice_Class/Reference/UIDevice.html
So it is probably that you get Unkown orientation because you never started the orientation notifications generation.
You should also log the value in your viewDidLoad to confirm exactly what you receive when you get the orientation. That would be a good starting point for further investigation.
Use this :-
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
//code for portrait
}
else
{ //code for Landscape
}
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationPortrait ||interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation is the delegate method oa uiviewcontroller.
According to the docs, the orientation property of UIDevice will always return 0 (i.e., UIDeviceOrientationUnknown) unless -beginGeneratingDeviceOrientationNotifications has been called first. This method will enable the accelerometer, deliver notification changes, and update the orientation property of the UIDevice singleton.

UIDeviceOrientationFaceUp - how to distinguish between portrait and landscape?

I'm trying to find out if the device is in portrait or landscape mode. My code works quite well if the device is not facing up. If it does face up (and orientation == 5), it won't distinguish between portrait and landscape. Is there anyway to determine the "orientation" in terms of landscape / portrait if the UIDeviceOrientation is FaceUp?
My code:
UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
NSLog(#"orientation: %d", interfaceOrientation);
if (interfaceOrientation == UIDeviceOrientationIsLandscape(interfaceOrientation)) {
NSLog(#"LANDSCAPE!!!");
}
if (interfaceOrientation == UIDeviceOrientationIsPortrait(interfaceOrientation)) {
NSLog(#"PORTRAIT!!!");
}
You should not confuse UIDeviceOrientation and UIInterfaceOrientation, they are different but related as shown by their declaration
typedef enum {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait,
UIDeviceOrientationPortraitUpsideDown,
UIDeviceOrientationLandscapeLeft,
UIDeviceOrientationLandscapeRight,
UIDeviceOrientationFaceUp,
UIDeviceOrientationFaceDown
} UIDeviceOrientation;
typedef enum {
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;
UIDeviceOrientation tells you what the orientation of the device is. UIInterfaceOrientation tells you what the orientation of your interface is, and is used by UIViewController. UIInterfaceOrientation will clearly be either portrait or landscape, whereas UIDeviceOrientation can have ambiguous values (UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown, UIDeviceOrientationUnknown).
In any case you should not attempt to determine the orientation of a UIViewController with [[UIDevice currentDevice] orientation], as no matter what the device orientation is the UIViewController interfaceOrientation property can be different (for example if your app does not rotate to landscape at all [[UIDevice currentDevice] orientation] can be UIDeviceOrientationLandscapeLeft while viewController.interfaceOrientation can be UIInterfaceOrientationPortrait).
Update:
As of iOS 8.0, [UIViewController interfaceOrientation] is deprecated. An alternative offered here is [[UIApplication sharedApplication] statusBarOrientation]. This also returns UIInterfaceOrientation.
I make this code skeleton for dealing with wanted & unwanted devices orientations, in my case i want to ignore the UIDeviceOrientationUnknown, UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown, caching the last allowed orientation. This code deals with iPhone and iPad devices and can be useful for you.
- (void)modXibFromRotation {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
NSString *device = [[UIDevice currentDevice]localizedModel];
UIInterfaceOrientation cachedOrientation = [self interfaceOrientation];
if ([device isEqualToString:#"iPad"]) {
if (orientation == UIDeviceOrientationUnknown ||
orientation == UIDeviceOrientationFaceUp ||
orientation == UIDeviceOrientationFaceDown) {
orientation = (UIDeviceOrientation)cachedOrientation;
}
if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
/* Your code */
}
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
/* Your code */
}
}
if ([device isEqualToString:#"iPhone"] || [device isEqualToString:#"iPod"]) {
if (orientation == UIDeviceOrientationUnknown ||
orientation == UIDeviceOrientationFaceUp ||
orientation == UIDeviceOrientationFaceDown) {
orientation = (UIDeviceOrientation)cachedOrientation;
}
if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
/* Your code */
}
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
/* Your code */
}
}
}
As of iOS13 use
UIInterfaceOrientation orientation;
if (#available(iOS 13.0, *)) {
orientation = self.window.windowScene.interfaceOrientation;
} else {
orientation = [[UIApplication sharedApplication] statusBarOrientation];
}

iPad Interface Orientation Problem

When user tap on the UIView (which is fullscreen) my app needs to detect orientation and do some stuff. But there is one tiny problem.
If I start application in landscape mode and user tap background 'interfaceOrientation' variable is '0' and I don't know how to rearrange view elements. If I rotate simulator once everything is fine but if not 'interfaceOrientation' is '0'. What to do here?
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
...
}
else if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
...
}
else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
...
}
I'm not sure how well that is solvable. UIDeviceOrientationUnknown is 0. This means, especially for the iPad which hasn't got a gyroscope, that the orientation at this time is simply not know. Imagine your iPad laying on a table, flat and the user is starting your application: there isn't any means to define that your application is actually running in landscape or portrait, unless you tilt the device accordingly. Hence.. the orientation at startup is always 0 (unknown).
You are casting a UIDeviceOrientation type to a UIInterfaceOrientation type. Device orientations have several different values beyond those of an interface orientation.
Try using:
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
switch (deviceOrientation) {
default:
case UIDeviceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
case UIDeviceOrientationUnknown:
//...
break;
case UIDeviceOrientationLandscapeLeft:
//...
break;
case UIDeviceOrientationLandscapeRight:
//...
break;
}
Edit: If the device orientation is unknown you should just set up your regular portrait view.
You should be able to access [UIApplication sharedApplication].statusBarOrientation if you can't access self.interfaceOrientation from your view controller.
Also, sometimes calling self.interfaceOrientation in viewDidLoad is risky because the view will load before it is aware of its orientation, so it will perform the rotation afterwards. In that case try finding it in viewWillAppear.
Or just override willRotateToInterfaceOrientation:duration and pass that information to the UIView that needs it. Note: that won't get called if you have a xib set to that orientation.

Getting wrong orientation

I am having an issue in my application which drives me mad. In my application, I rotate the simulator to the landscape mode, but in my below function, I get portrait orientation.
What is the problem here? Please help me out.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ( interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown )
{
NSLog(#" portrait orientation");
}
else
{
NSLog(#"Landscape");
}
return YES;
}
First set the orientation value is the method
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
NSLog(#"shouldAutorotateToInterfaceOrientation called...");
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)
defaultOrientation = 0;
else if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight)
defaultOrientation = 1;
[self setTheOrientation];
return YES;
}
Now set the values of the coordinates which you require according to the boolean values in the setTheOrientation method.
The method returns BOOL, you have to return either YES or NO
Why you did't return a BOOL value? YES or NO to tell the OS that you're gonna handle corresponding orientation events.
If you want portrait mode then add return TRUE in if clouse and If you want landscape then add return TRUE in else clouse and if you want both mode then just type return TRUE in shouldAutoRotate clouse

How can I query the current orientation of an iPhone/iPad screen?

I was curious to know if there is some way to determine the orientation of a screen on iPhone/iPad.
Currently I find myself setting a member variable when this message is called:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
self.orientation = interfaceOrientation;
return YES;
}
... which feels like something I should be able to query as a state instead of having to track it when it changes.
UIViewController has the interfaceOrientation property.
Don't get confused by orientation in UIDevice, which is not the orientation of the interface, but the physical orientation.
Use this if you have a status bar on top.
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
// portrait
} else {
// landscape
}
You can do [[UIDevice currentDevice] orientation].