How to handle different orientations in iOS
Using -[UIDevice setOrientation:]
is a private API, and will get your application rejected. See this question.
What you ask is not possible using public API and is also not recommended from HIG standpoint. What is supported and you should implement, is modal presentation of the different view controllers with different supported interface orientation. This is why the default implementation of UINavigationController
is to always rotate; it assumes all view controllers have the same supported interface orientations.
Take for example video playback on iPhone. Open the video apps (that comes with iOS). The root view controller only supports portrait orientation. However, start a video, and a modal view controller pops up which only supports landscape interface orientations. This seems exactly the behavior you wish to achieve.
This is why preferredInterfaceOrientationForPresentation
is not called. preferredInterfaceOrientationForPresentation
only gets called when using presentViewController:animated:
.
A small gotcha, if you require a navigation bar in each stage of your scene, you will need to enclose each modal view controller with a navigation controller. You can then pass the required data in prepareForSegue:
by accessing topViewController
of the navigation controller object in the segue.
Here is an example project which behaves correctly according to your requirements (or at least will give you ideas how to implement):
http://www.mediafire.com/?zw3qesn8w4v66hy
What is the right way to handle orientation changes in iOS 8?
Apple recommends using size classes as a coarse measure of how much screen space is available, so that your UI can significantly change its layout and appearance. Consider that an iPad in portrait has the same size classes as it does in landscape (Regular width, Regular height). This means that your UI should be more or less similar between the two orientations.
However, the change from portrait to landscape in an iPad is significant enough that you may need to make some smaller adjustments to the UI, even though the size classes have not changed. Since the interface orientation related methods on UIViewController
have been deprecated, Apple now recommends implementing the following new method in UIViewController
as a replacement:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id )coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// Code here will execute before the rotation begins.
// Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]
[coordinator animateAlongsideTransition:^(id context) {
// Place code here to perform animations during the rotation.
// You can pass nil or leave this block empty if not necessary.
} completion:^(id context) {
// Code here will execute after the rotation has finished.
// Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]
}];
}
Great! Now you're getting callbacks right before the rotation starts, and after it finishes. But what about actually knowing whether the rotation is to portrait or to landscape?
Apple recommends thinking about rotation as simply a change in size of the parent view. In other words, during an iPad rotation from portrait to landscape, you can think of it as the root-level view simply changing its bounds.size
from {768, 1024}
to {1024, 768}
. Knowing this then, you should use the size
passed into the viewWillTransitionToSize:withTransitionCoordinator:
method above to figure out whether you are rotating to portrait or landscape.
If you want an even more seamless way to migrate legacy code to the new iOS 8 way of doing things, consider using this simple category on UIView, which can be used to determine whether a view is "portrait" or "landscape" based on its size.
To recap:
- You should use size classes to determine when to show fundamentally different UIs (e.g. an "iPhone-like" UI vs. an "iPad-like" UI)
- If you need to make smaller adjustments to your UI when size classes don't change but your container (parent view) size does, such as when an iPad rotates, use the
viewWillTransitionToSize:withTransitionCoordinator:
callback in UIViewController. - Every view in your app should only make layout decisions based on the space that it has been given to layout in. Let the natural hierarchy of views cascade this information down.
- Similarly, don't use the
statusBarOrientation
-- which is basically a device-level property -- to determine whether to layout a view for "portrait" vs "landscape". The status bar orientation should only be used by code dealing with things likeUIWindow
which actually live at the very root level of the app.
iOS - different screen orientations for different view controllers
Ok, just in case this interests anyone, this was my solution, using the accepted answer here:
The thing I was missing was in my approach - The landscape VC can't be under the same root VC as the portrait ones, it needs to be or have its own root VC, which is in landscape.
So first, I separated the landscape VC from the rest in the storyboard, now it's completely independent. Next, I created a "view controller switch" method, which basically loads a new controller, sets it to be the root controller, and releases the previous root controller:
+(void)loadController:(UIViewController *)VControllerToLoad andRelease:(UIViewController *)VControllerToRelease
{
//adjust the frame of the new controller
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect windowFrame = [[UIScreen mainScreen] bounds];
CGRect firstViewFrame = CGRectMake(statusBarFrame.origin.x, statusBarFrame.size.height, windowFrame.size.width, windowFrame.size.height - statusBarFrame.size.height);
VControllerToLoad.view.frame = firstViewFrame;
//set the new controller as the root controller
[[[UIApplication sharedApplication].delegate window] setRootViewController:VControllerToLoad];
//kill the previous view controller
[VControllerToRelease.view removeFromSuperview];
}
In the landscape VC I added this code:
-(BOOL)shouldAutorotate
{
return YES;
}
And whenever I need to present the landscape VC or go back the the portrait VCs, I just use the VC switch method. For example:
[AppUtils loadController:landscapeViewController andRelease:portraitNavigationController];
That's it! Now everything works like a charm! :)
How to Handle Orientation in IOS Objective C only for one screen and reset when press
Add below code inside viewWillAppear on all viewControllers you want to show portrait only. don't forget to mark Device Orientation to both Portrait and Landscape modes.
NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
The best way to handle different orientations in an iPad
You can implement two views in one xib and change which is visible in rotation, but this results in requiring twice as many IBOutlet
s one for each property for each view.
If it is difficult layout changes I would probably change the xib, but for simple stuff (where the autosizing resizing isn't working great) I often just set the frame to the new position in code. Keep in mind this create a nice chunk of code that is not ro pretty to look at. I will often mock the view up in IB
still so I can get the frame positions to move to.
Different orientation for iPhone and iPad in one storyboard
Open your info.plist file as code and paste this code:
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
Works well for me!
Different layouts in portrait and landscape mode
Note - the answers here are good and do address the problem, but on older versions of iOS.
For iOS11 (Xcode 9) you should consider Adaptive Layouts as referenced here:
https://www.raywenderlich.com/162311/adaptive-layout-tutorial-ios-11-getting-started
Related Topics
Set Initial Viewcontroller in Appdelegate - Swift
Get the Current Scroll Position of a Swiftui Scrollview
Uiscrollview Pauses Nstimer Until Scrolling Finishes
Print: Entry, ":Cfbundleidentifier", Does Not Exist
I Have Real Misunderstanding With Mfmailcomposeviewcontroller in Swift (Ios8) in Simulator
Drawing a Route in Mapkit in Ios
Basic Example For Sharing Text or Image With Uiactivityviewcontroller in Swift
Uitapgesturerecognizer - Single Tap and Double Tap
How to Animate the Textcolor Property of an Uilabel
Custom Edit View in Uitableviewcell While Swipe Left. Objective-C or Swift
Iphone Get a List of All Ssids Without Private Library
Trying to Handle "Back" Navigation Button Action in Ios
How to Test Apple Push Notification Service Without an Iphone
Ios9 Getting Error "An Ssl Error Has Occurred and a Secure Connection to the Server Cannot Be Made"
How to Iterate For Loop in Reverse Order in Swift