My app can autorotate but I need one of the views to only show in portrait mode and don't know how to achieve this.
I tried this (among other things) but the view in question still rotates:
// ViewController.m
-(BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Can someone kindly point out what I'm doing wrong? Thanks.
-edit-
It's for iOS 6.1
When a UINavigationController is involved, create a category on the UINavigationController and override supportedInterfaceOrientations.
#import "UINavigationController+Orientation.h"
#implementation UINavigationController (Orientation)
- (NSUInteger)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
- (BOOL)shouldAutorotate {
return YES;
}
#end
Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate.
How to create a category
1. Add a new file (Objective c- category under cocoa touch)
2. Category : Orientation on UINavigationController
3. Add the above code to UINavigationController+Orientation.m
Swift 3 version the accepted answer:
extension UINavigationController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// Change `.portrait` to whatever your default is throughout your app
return topViewController?.supportedInterfaceOrientations ?? .portrait
}
open override var shouldAutorotate: Bool {
return true
}
}
As per the documentation.
A view controller can override the supportedInterfaceOrientations method to limit the list of supported orientations.
So we need to override shouldAutorotate and supportedInterfaceOrientation to target view controllers.
Typically, the system calls this method only on the root view controller of the window or a view controller presented to fill the entire screen.
This will work if you have very simple configuration like your target view controller is the rootViewController of window or being presented covering whole screen.
In case when configuration of target view controller is complex like embedded in some other container view controllers.
child view controllers use the portion of the window provided for them by their parent view controller and no longer participate directly in decisions about what rotations are supported.
So may be default implementation of these container view controllers not asking there children for there supportedInterfaceOrientation preference.
So to allow our target child view controller to specify there supportedIntefaceOrientation we need to tell there container view controller to do so.
You can also check my previous answer here.
and Understanding UIViewController rotation when embed in Container View Controllers.
Related
I am developing an application which is in portrait mode.
But I want one view controller should display in landscape as well as in portrait mode.
I tried the following code but it doesn't work (not called).
- (BOOL) shouldAutorotate
{
return NO;
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationMaskPortrait;
}
First, you need to set in your plist all the orientations your app supports, this can be done in the 'General' tab in the project under "Deployment Info", for example:
Then, you can use the method supportedInterfaceOrientations,
I assume you are presenting the view controller modally, so simply override it, on the presenting viewController, which need to be only in portrait use:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
and in your presented viewController, which should also supports landscape, use: (or whatever orientation mask you would like)
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
P.S - there is a different behavior for viewController that is presented modally and for a viewController that push in a navigationController stack:
modalViewController will call its own supportedInterfaceOrientations, and will support these orientations
pushedViewController will call its navigationController supportedInterfaceOrientations, and will support these orientation.
So, if you are presenting the viewController modally, you need to override its own supportedInterfaceOrientations, but if you push this viewController, you need to set some BOOL property in the navigationController, so it will know which orientations to supports.
I advise you to present this viewController modally, it's more natural to use modalViewController for different device orientations.
P.S #2: about shouldAutorotate: if it returns 'NO', than supportedInterfaceOrientations is not called, so return 'YES'. It only says, if to rotate automatically when the device rotates. if it returns 'NO', you need to explicit rotate the viewController.
Well I hope I helped and didn't write an answer that is completely not regarded to what you asked... :)
My app can autorotate but I need one of the views to only show in portrait mode and don't know how to achieve this.
I tried this (among other things) but the view in question still rotates:
-(BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
This solution explains how to control orientation on individual view controllers, provided they are managed by a navigation controller.
In Xcode 5, create a new file of type "Objective-C category", set it's "Category" to "rotation" and choose "UINavigationController" as "Category on".
A new file couple will appear in the project, having the following names:
UINavigationController+rotation.h
UINavigationController+rotation.m
In the .m file, write the following code:
- (BOOL) shouldAutorotate
{
return [[self topViewController] shouldAutorotate];
}
- (NSUInteger) supportedInterfaceOrientations
{
return [[self topViewController] supportedInterfaceOrientations];
}
This way, the navigation controller will let the current top view controller determine the orientation policy.
Then, in each specific view controller that is managed by the navigation controller, you can override the two orientation-related methods.
For instance, if a specific view controller shall appear in portrait orientation only:
- (BOOL) shouldAutorotate
{
return NO;
}
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Make sure that the desired orientation is one of those set in the project deployment info.
Hope this is sufficiently detailed and can be of help.
supportedInterfaceOrientations will work if you present your view controller as a modal view controller. It won't work if you present it as part of a navigation controller stack. If you want your view presented modally but inside a navigation controller (to have navigation items, for instance) the solution I did was to subclass UINavigationController and override the supportedInterfaceOrientations methods on my subclass.
So, I want to lock the orientation of my home page to portrait, and the Home page ONLY.
I am using a tab bar controller, so the initial view is the tab controller, but the view controller that appears first is the first tab, e.g. the Home page.
I would like to make it so that when the user goes to rotate the device, it WILL NOT rotate to landscape on this page. However all other pages can rotate.
I have searched around, and nothing seems to be specific to iOS 7, and the one that is specific to iOS 7 doesn't work…
Please help, thank you!
The image below describes what I DON"T want to happen, for this page.
Implement the following in your implementation
- (NSUInteger) supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
This should give you the results you are looking for!
Use this code
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return UIInterfaceOrientationMaskPortrait;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
#end
The problem is, as you've rightly pointed out, that your home tab is not the topmost view controller.
From my limited knowledge on the subject I can only think of the following:
Create another tab view controller and implement the methods to control orientation, i.e. shouldAutorotate and supportedInterfaceOrientations;
Make this controller the first one at startup;
Route the other tabs down to the original tab controller (the one that supports all orientations) using a push segue.
I think I found a nice solution.
Well, in my case I'm using a UISplitViewController as rootController in the storyboard but the idea is the same.
SubClass your rootController (In my case UISplitViewController) and Catch the shouldAutorotate() callback so you can call subviews shouldAutorotate from there.
Implement shouldAutorotate() in the View you want to Lock the Rotation
class MyUISplitViewController: UISplitViewController {
override func shouldAutorotate() -> Bool {
if ((self.viewControllers.last) != nil && (self.viewControllers.last!.topViewController) != nil){
if (self.viewControllers.last!.topViewController!.respondsToSelector("shouldAutorotate"))
{
return self.viewControllers.last!.topViewController!.shouldAutorotate()
}
}
return true
}
}
In your sub UIViewController
override func shouldAutorotate() -> Bool {
if (UIDevice.currentDevice().userInterfaceIdiom == .Phone)
{
return false
}else{
return true
}
}
If you want to check the supported orientations, you can simply do the same with supportedsupportedInterfaceOrientations()
EDIT:
Don't forget to set your "MyUISplitViewController" class in your Storyboard root viewController
I have th e following code in my uiviewcontroller.m file:
-(BOOL)shouldAutoRotate
{
return YES or NO;
}
I have tried both YES and NO but my view controller still rotates? I am using iOS 7 and the other iOS solutions I found aren't working for me either.
It probably happens because your controller instantiated as child of UINavigationController in view hierarchy. The UINavigationController does not query child controllers if they want to be rotated or not.
I had the same issue; I wanted to disable autorotation, so all hierarchy of particular UINavigationController is locked in Portrait. I ended with this class:
#implementation FixedOrientationNavigationController
- (BOOL)shouldAutorotate {
return NO;
}
#end
which I put instead of UINavigationControllr class in Storyboard for hierarchies which I need to lock Portrait. Just this, I do not need to implement shouldAutorotate in each controller.
You may also check this link: Orientation Respectful UINavigationController, it tries to implement "orientation respectful" UINavigationController.
It works, but in some cases it leads to weird results, for example, when user rotate to Landscape and then go back to the controller which should only support Portrait.
You can also set the orientation by clicking on project name and then general ,here you can set the orientations you want and set
- (BOOL)shouldAutorotate {
return NO;
}
Hope you got.
I have a navigation controller which have a few view controllers. I need to support all orientations for all view controllers except one special view controller which only supports landscape. This special view controller appears in the middle of the navigation stack. I have done quite a lot of research but couldn't find any good solution. Here are the links that I have read and tried.
http://www.iphonedevsdk.com/forum/iphone-sdk-development/3219-force-landscape-mode-one-view.html#post60435
How to rotate screen to landscape?
How to autorotate from portrait to landscape mode?
iPhone - allow landscape orientation on just one viewcontroller
http://goodliffe.blogspot.com/2009/12/iphone-forcing-uiview-to-reorientate.html
Next I am going to try to replace navigation controller with presentModalViewController in order to display the special view controller. Then I am going to create a new navigation view controller inside the special view controller to push the subsequent view controllers.
If anyone has a better idea, please let me know. Really appreciated!
UPDATE: I have successfully use the method I described above: replace pushViewController with presentModalViewController and create a new navigation controller.
Every view controller pushed onto the navigation controllers stack have to support the same orientations. This means that it is not possible to have some view controllers only supporting portrait and others only supporting landscape. In other words all view controllers on the same navigation controller stack should return the same in the delegate:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
But there is a simple solution to this! Here is an example for going from portrait to landscape. Here is the steps to do it and below is code to support it.
Create a ‘fake’ view controller that will be root in a sub navigation controller. This view controller should support landscape.
Create a new instance of a UINavigationController, add an instance of the ‘fake’ view controller as root and an instance of your landscape view controller as second view controller
Present the UINavigationController instance as modal from the parent view controller
First, create a new view controller (FakeRootViewController) with this code:
#interface FakeRootViewController : UIViewController
#property (strong, nonatomic) UINavigationController* parentNavigationController;
#end
#implementation FaceRootViewController
#synthesize parentNavigationController;
// viewWillAppear is called when we touch the back button on the navigation bar
(void)viewWillAppear:(BOOL)animated {
// Remove our self from modal view though the parent view controller
[parentNavigationController dismissModalViewControllerAnimated:YES];
}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
Here is the code to present the view controller that you wish to show in landscape mode:
FakeRootViewController* fakeRootViewController = [[FakeRootViewController alloc] init];[fakeRootViewController.navigationItem setBackBarButtonItem:backButton]; // Set back button
// The parent navigation controller is the one containing the view controllers in portrait mode.
fakeRootViewController.parentNavigationController = parentNavigationController;
UINavigationController* subNavigationController = // Initialize this the same way you have initialized your parent navigation controller.
UIViewController* landscapeViewController = // Initialize the landscape view controller
[subNavigationController setViewControllers:
[NSArray arrayWithObjects:fakeRootViewController,
landscapeViewController, nil] animated:NO];
[_navigationController presentModalViewController:subNavigationController animated:YES];
Remember that the landscapeViewController should also have this implementation:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
There's a private API to force an orientation change. Put in your pushed view controller's -viewWillAppear::
if ([UIDevice instancesRespondToSelector:#selector(setOrientation:)]) {
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}
To suppress the compiler warning, add this to the .m file of your view controller:
#interface UIDevice()
- (void)setOrientation:(UIDeviceOrientation)orientation; // private API to let POIEntryVC be pushed over landscape route view
#end
As always, there's a risk of being rejected and a risk of breaking in future OS versions when using private APIs. Do at your own risk!
Generally, presenting a modal view controller is the better solution in most cases.
You can make actions:
Change Your code with accordance of schellsan suggestion, next -
Try to add currentViewController(which will push to navigation viewController) as property to appDelegate. When You attempt to push view controller, set it to current view controller before this. Next - make a subclass of rootViewController in navigation controller. In this subclass owerload method
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return [appDelegate.currentViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
It should works if You not using a navigation bar and pushes new controller after popping an old
It should be as simple as implementing
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
in each UIViewController pushed into your UINavigationController. In the case that one UIViewController's view shouldn't rotate, return NO for that specific orientation in that specific UIViewController.
There's a gotcha here though, if your UINavigationController implements
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
it will block its viewControllers from receiving that method. In that case, you should forward the message to the topViewController using
[[self topViewController] shouldAutorotateToInterfaceOrientation:interfaceOrientation];
You could try this code in your UINavigationController to call the current visible view's shouldAutorotateToInterfaceOrientation. In my case I have the UINavigationController in a UITabBarController but you could probably adapt it to other cases.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([appDelegate.tabBarController.selectedViewController respondsToSelector:#selector(topViewController)])
{
UINavigationController *nc = (UINavigationController*)appDelegate.tabBarController.selectedViewController;
return [nc.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}