I want my iPhone app to globally behave as if only the UIInterfaceOrientationPortrait was permitted.
But at the same time I want it to be aware of the physical orientation in order to react in a specific way, only within a limited area of the display.
How can I get this?
I did some tests using the self.interfaceOrientation iVar of the UIViewController, but that does not seem to work, because this variable does not change.
First, do [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]. Then register for UIDeviceOrientationDidChangeNotification. When you receive the notification, check [UIDevice currentDevice].orientation for the device's physical orientation.
You should definitely read the documentation for these APIs, because it contains some important warnings.
Also note that device orientation is returned as a UIDeviceOrientation, which is different than a UIInterfaceOrientation. Keep in mind this information from the UIInterfaceOrientation documentation:
You use these constants in the statusBarOrientation property and the setStatusBarOrientation:animated: method. Notice that UIDeviceOrientationLandscapeRight is assigned to UIInterfaceOrientationLandscapeLeft and UIDeviceOrientationLandscapeLeft is assigned to UIInterfaceOrientationLandscapeRight; the reason for this is that rotating the device requires rotating the content in the opposite direction.
Related
What's the Difference between Interface Orientation and Device Orientation?
Both of them seems the same but what's the actual difference between them?
Interface Orientation can be anything, regardless of device orientation. Device Orientation is the actual physical orientation of the device you are holding, and this is not variable; it is what it is. If you are holding in Portrait, it is Portrait. But just because the device is in Portrait doesn't mean your interface is also in portrait. You may require your app to only provide landscape orientation, hence the interface orientation would be different than the device orientation.
Following description got from this site.
UIDeviceOrientation is a property of the UIDevice class, and there are these possible values:
UIDeviceOrientationUnknown - Can't be determined
UIDeviceOrientationPortrait - Home button facing down
UIDeviceOrientationPortraitUpsideDown - Home button facing up
UIDeviceOrientationLandscapeLeft - Home button facing right
UIDeviceOrientationLandscapeRight - Home button facing left
UIDeviceOrientationFaceUp - Device is flat, with screen facing up
UIDeviceOrientationFaceDown - Device is flat, with screen facing down
As for UIInterfaceOrientation, it is a property of UIApplication and only contains 4 possibilities which correspond to the orientation of the status bar:
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
(copied from docs)
For example, the device (the actual iPhone or iPod) could be in UIDeviceOrientationLandscapeRight but if your app doesn't support that, it (your app) might still be in UIInterfaceOrientationPortrait.
In addition, to get UIDeviceOrientation, you use [[UIDevice currentDevice] orientation], and to get UIInterfaceOrientation, you use [[UIApplication sharedApplication] statusBarOrientation]
More info
I want to have my app always in portrait mode facing up. That means that if the device it tipped to passed perpendicular to the floor and is starting to face the floor, it will readjust so it functions like it is in portrait mode facing upward. The same with if the device starts to be portrait upside-down, it will be portrait facing up (tilted upward. I have placed this in my apps delegate
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
under the -(BOOL) application did finishLaunching etc. but I need to know what I need to put into the delegate so throughout the app the above is always true.
Anyone help.
You specifying all required orientations in your Info.plist file as Array with a key UISupportedInterfaceOrientations Limit the list of supported orientation to the orientation that you want. beginGeneratingDeviceOrientationNotifications is very specific method that required for a unique use-cases but not for general orientation handling. Here is some of examples where beginGeneratingDeviceOrientationNotifications could be useful, but it is definitely not your case - How to handle autorotation in AVCaptureVideoPreviewLayer?
You can set this in your project's deployment info in Xcode.
I think you also need to implement shouldAutorotate and supportedInterfaceOrientations in your UIViewControllers. This Apple reference doc should help: UIViewController Class Reference.
Also, here's a similar question and answer.
I am looking for some clarification on how to allow only certain orientations for your iOS app. I am aware of UISupportedInterfaceOrientations and shouldAutorotateToInterfaceOrientation but I am a little confused on their uses and exactly how they fit together.
I attempted to use UISupportedInterfaceOrientations to only allow landscape orientations, which appeared to have no affect until I researched in to it and read that it affects the initial orientation. Upon testing this, my app does appear to only open in landscape but quickly rotates if the screen is portrait.
I know you can use shouldAutorotateToInterfaceOrientation to limit the allowed orientations, for example:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
However, while doing some reading online I read shouldAutorotateToInterfaceOrientation is deprecated as of iOS6.
Basically my questions are:
What is the correct approach for limiting screen orientations across
multiple versions of iOS?
Is the only use of UISupportedInterfaceOrientations to limit the
initial orientation?
Edit:
To expand on the accepted answer, shouldAutorotate works in iOS6. As a quick fix if you've already implemented your logic in shouldAutorotateToInterfaceOrientation and/or you want to support earlier versions of iOS, you can do the following:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (BOOL)shouldAutorotate {
return [self shouldAutorotateToInterfaceOrientation:self.interfaceOrientation];
}
The method you need to use for rotation instead of shouldAutorotateToInterfaceOrientation is just shouldAutorotate
Handling rotation, according to the AppleDoc for ViewControllers:
In iOS 6, your app supports the interface orientations defined in your app’s Info.plist file. A view controller can override the supportedInterfaceOrientations method to limit the list of supported orientations. Generally, the system calls this method only on the root view controller of the window or a view controller presented to fill the entire screen; child view controllers use the portion of the window provided for them by their parent view controller and no longer participate in directly in decisions about what rotations are supported. The intersection of the app’s orientation mask and the view controller’s orientation mask is used to determine which orientations a view controller can be rotated into.
You can override the preferredInterfaceOrientationForPresentation for a view controller that is intended to be presented full screen in a specific orientation.
The method shouldAutorotateToInterfaceOrientation is deprecated, as are some methods for handling responses to device rotation.
For supporting methods of multiple versions of iOS, Here's something else that Apple has said:
For compatibility, view controllers that still implement the shouldAutorotateToInterfaceOrientation: method do not get the new autorotation behaviors. (In other words, they do not fall back to using the app, app delegate, or Info.plist file to determine the supported orientations.) Instead, the shouldAutorotateToInterfaceOrientation: method is used to synthesize the information that would be returned by the supportedInterfaceOrientations method.
Taken from release notes
To answer your second question:
Yes, the "UISupportedInterfaceOrientations" entry in the Info.plist is used only for initial startup of your app, making sure it doesn't start your app in an orientation it doesn't support, so that it's not required to perform a rotation right away.
Also, overriding "application:supportedInterfaceOrientationsForWindow" in your AppDelegate is pretty useful if your app never wants to use particular orientations (e.g. for a game that only does landscape).
Lastly, and this is a common error, on iPhone and iPod Touch devices, the device should never rotate to UIInterfaceOrientationPortraitUpsideDown! That's because these devices (contrary to an iPad) do not let the user lock a device in landscape mode with the Lock soft button - that button only locks into portrait. Therefore, if a user, laying on his side, wants to use an app in landscape mode, he cannot do this if your app goes into the upside down orientation. But if you disallow that rotation, then it works.
How does one detect the correct orientation of the application on the first view? I need to know so I can either display a portrait image or landscape image as the background.
[[UIDevice currentDevice] orientation] doesn't work properly if the iPad is laid down flat on a surface.
statusBarOrientation doesn't seem to return the correct results for the very first view that is loaded. I tried getting the status bar orientation in the viewWillAppear and viewDidLoad and both returned Portrait even when in landscape.
I have confirmed that I have [self.window makeKeyAndVisible] in my AppDelegate.m in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions. This is only returning the incorrect status bar orientation on the first view controller.
Note: I have also tried the interfaceOrientation from the UIViewController, ad that still returns portrait no matter what.
Any ideas how to fix this?
EDIT:
I am using the [[UIDevice currentDevice] orientation] to detect if it is portrait or landscape but my issue is detecting what the view actually is if UIDevice returns an orientation of flat (non portrait or landscape).
Many applications have this issue. If the device is flat then any of the orientations is valid.
Your best bet is to use the [[UIDevice currentDevice] orientation] to know what the orientation is if possible for the cases where the orientation can be determined. But when it's flat, it'll just be whatever it was last.
How many times have you see people flick up their iPad to get the landscape orienation the right way around.
GOT IT!
I was able to do a work around by using
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
Even on view load this method is triggered if the orientation is not of UIInterfaceOrientationPortrait.
Even if the device is flat, it seems that there is a long enough delay for the orientation to correct itself and detect whether the UIView is in portrait or landscape mode.
EDIT:
When viewDidAppear is triggered by then, [self interfaceOrientation] has the correct value. So if you can wait until that method, you can also get it there.
The [[UIDevice currentDevice] orientation] method returns a number of orientations beyond the portrait and landscape orientation. I am well aware of checking to see if the orientation returned is "valid", unfortunately if the orientation returned is NOT "valid" when my app requires a specific orientation to determine which methods to run, I have no way of knowing which method is appropriate.
I have attempted a number of solutions to this, but for now the only thing I have been able to work out is to check to see if the orientation is LandscapeLeft or LandscapeRight and if it isn't I assume the screen is in portrait orientation. Unfortunately this isn't true when the device is face up and the screen is in landscape orientation.
I attempted to use the parent view controller's orientation:
parentController.interfaceOrientation;
Unfortunately it returned UIDeviceOrientationUnknown. I search SO and Google for someone else who has faced this problem. It seems silly to me that Apple would have anything but LandscapeLeft/Right and PortraitUp/Down for their orientations.
What I really need is the APPS orientation not the devices, since these are sometimes at odds. Any advice?
Have you tried using UIInterfaceOrientationPortrait/UIInterfaceOrientationLandscape? I usually have pretty good results using these methods:
if(UIInterfaceOrientationIsPortrait(viewController.interfaceOrientation)) {
//do portrait work
} else if(UIInterfaceOrientationIsLandscape(viewController.interfaceOrientation)){
//do landscape work
}
You might also try UIDeviceOrientationIsValidInterfaceOrientation.
These will only return the app's orientation, which may not be the same as the device's orientation.
If those don't work, you can also try:
[[UIApplication sharedApplication] statusBarOrientation]