At this time I am halfway to keeping ViewControllerA in portrait mode. To do this, I am using preferredInterfaceOrientationForPresentation
This ensures that ViewControllerA, when I push to it, will be in portrait mode. However, if I push from ViewControllerA to ViewControllerB, switch to landscape mode in ViewControllerB, and then dismiss back to ViewControllerA, ViewControllerA can be presented in landscape mode. My desire is to continue multiple orientation support of ViewControllerB, but force ViewControllerA to autorotate to portrait.
Also, for some reason shouldAutorotate does not appear to be getting called in ViewControllerA. Perhaps addressing this could fix the entire issue?
UINavigationController+Orientation.h Category
#interface UINavigationController (Orientation)
- (BOOL)shouldAutorotate;
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
#end
UINavigationController+Orientation.m Category
-(BOOL)shouldAutorotate {
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
ViewControllerA.m
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
I faced the same problem. My solution is below, and its in a released app.
I added a public property called allowViewRotation to my AppDelegate.h:
#property (assign, nonatomic) BOOL allowViewRotation;
Then I've implemented the following call in my AppDelegate.m file:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { // if iPad
return UIInterfaceOrientationMaskAll;
}
if (self.allowViewRotation) {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
else {
return UIInterfaceOrientationMaskPortrait;
}
}
Now in ViewControllerA set allowViewRotation property to NO
((AppDelegate*)[UIApplication sharedApplication].delegate).allowViewRotation = NO;
And in ViewControllerB set allowViewRotation property to YES
((AppDelegate*)[UIApplication sharedApplication].delegate).allowViewRotation = YES;
Note: make sure that you enable the proper device orientation in Deployment Info
hope this helps!
Alright, I was able to get things working!
So one thing in my situation that was making things difficult was I had two segments; one took place before logging in (did not have NavController) an the other was post login (did have NavController). Perhaps I broke a best practice guideline there?
Anyways, Teo C.'s answer is the one that helped me: iOS 7. Change page orientation only for one view controller
I made this discovery by making a new blank project. If you are having trouble with forcing orientations, you can look at the example I made https://github.com/jerherrero/Force-Orientations
Related
I have an app that is always showed in portrait mode.
But in somewhere i have a media gallery that must support landscape.
The supported orientation by default in the project is portrait. Because of that the gallery is only showed in portrait mode.
If i change the project setting to show in portrait and landscape the gallery works fine but i can't control the other viewControllers to show only in portrait.
I tried several methods like shouldAutoRotate but no one worked.
Any ideia how to solve?
Regards
EDIT:
Solved :)
First i configured the project to support all orientation.
Then i added this method to the AppDelegate.m:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape;
}
After this what i did was to block orientation in each view controller, less the one i want to have orientation in landscape and portrait.
Code to block orientation (iOS 7):
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskPortrait);
}
Thanks to everyone that answered me :)
In my app for iPhone its only support the portrait view only, but as per requirement need to support landscape view only for on view, at that time I use following way and its help me :
In your app delegate .h
#interface PlayWithWSWithLibAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
BOOL flagOrientationAll;
}
#property (assign) BOOL flagOrientationAll;
Add following method in your app delegate .m file
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
//NSLog(#"PlayWithWSWithLibAppDelegate -- supportedInterfaceOrientationsForWindow");
if([UICommonUtils isiPad]){
return UIInterfaceOrientationMaskAll;
}else if(flagOrientationAll == YES){
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
Implement following way in your view which you want to rotate in both portrait and landscape both for iPhone device
-(void)viewWillAppear:(BOOL)animated
{
self.tabBarController.delegate = self;
PlayWithWSWithLibAppDelegate *delegate = (PlayWithWSWithLibAppDelegate *) [[UIApplication sharedApplication] delegate];
delegate.flagOrientationAll = YES;
}
}
-(void)viewWillDisappear:(BOOL)animated
{
//NSLog(#"viewWillDisappear -- Start");
PlayWithWSWithLibAppDelegate *delegate = (PlayWithWSWithLibAppDelegate *)[[UIApplication sharedApplication] delegate];
delegate.flagOrientationAll = NO;
}
see this post also: How to set one of the screens in landscape mode in iphone?
You have to make another class in the same view Controller where you are presenting your media.
In that you can specify your orientation where it will support only landscape orientation only when you will present your media.
I am giving you an example of my app which supports only in landscape mode but as I took Image picker and it supports only in portrait mode so I changed the orientation for only that view.
#pragma mark - Image PICKER
#interface NonRotatingUIImagePickerController : UIImagePickerController
#end
#implementation NonRotatingUIImagePickerController
- (BOOL)shouldAutorotate
{
return YES;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait ;
}
#end
Then when I used ImagePicker I used object of the class that i made.
So I define like below.
UIImagePickerController* picker = [[NonRotatingUIImagePickerController alloc] init];
So For Image Picker It showed only in portrait mode.
You just need to change Landscape instead of portrait if you want only Landscape Orientation for your particular view where you want to change it.
Hope this helps you.
i am working on app. the orientation of app is landscape but after app run interface orientation of app change the interface and rotate. splash screen display in correct way (landscape).
i am using ios7 the app was code for ios5 i think there is some deprecated api issue e.g. shouldAutorotateToInterfaceOrientation bot called because this is no more available in latest ios
If you want all of our navigation controllers to respect the top view controller you can use a category so you don't have to go through and change a bunch of class names.
#implementation UINavigationController (Rotation_IOS6)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
As a few of the comments point to, this is a quick fix to the problem. A better solution is subclass UINavigationController and put these methods there. A subclass also helps for supporting 6 and 7.
you have to set orintatoin in build seeting see image.
it will solve your Problem.
Try this:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
EDIT :
See the attached link, might be helpful for you.
i find solution the think which i do is.
step one
override my UInavigationcontroller by creating a category
step two
Replace
[self.window addSubview:[navigationController view]]; //OLD
With
[self.window setRootViewController:navigationController]; //NEW
use this in your Appdelegate.m
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSLog(#"Interface orientations");
if([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPad ){
return UIInterfaceOrientationMaskLandScape;
}
else{
return UIInterfaceOrientationMaskPortrait;
}
}
It helped me..
ADDED:
You can access this project on github
ios6rotations
Sorry guys for asking the question about screen rotation in iOS 6 but this is really a pain in the ass..and I still can't understand it completely - for some reason it behaves differently under certain circumstances.
I have the following simple hierarchy of views in my test app:
What I'm trying to achieve is - to keep blue controller in landscape only and red one is only in portrait.
I have a subclass of UINavigationController with such code inside:
#implementation CustomNavController
- (BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
In my blue controller I implemented this:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
And in red controller this:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
Now I have the following behavior:
App started in landscape (OK)
When I press the button my red controller pushed in landscape too (this is not ok because it must be shown in Portrait)
It successfully rotates to portrait but not backward to landscape
If I leave the red controller in Portrait mode my blue controller (which is restricted to landscape) shows in Portrait mode.
P.S.
All my rotation methods(posted above) are getting called normally.(by the way why do these methods getting called so many times per screen transition - 5-6 times)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation does not getting called with pushing
All(except portraitUpsideDown) orientations are included in plist.
The question is - how to force rotation to supported orientation in each controller?
I suggest you to post here (as answers) any 100% working code to handle rotations in ios6 (for example if you have some for iPad with SplitController) - I'll keep this question in favorites to have all in one place when I need to handle some specific situations. Cheers!
ADDED:
Please do not post this as answer from landscape to portrait I hope that there'
s more elegant way to do it.
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
My two cents worth.
You can present an empty transparent modal view quickly then dismiss it, maybe on ViewDidLoad: or viewWillAppear: on your ViewController and ViewControllerSecond class as a quick workaround.
Also, in storyboard, you can set ViewController class orientation to landscape visually.
use this line for programmatically change orientation... work 100%
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
and also when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file.. at the top.
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return your supported orientations
if (currentMainView==blueOne) {
return toInterfaceOrientation== UIInterfaceOrientationPortrait;
}
}
I have a similar situation in one of my apps (although do note that I am not using UINavigationController).
Change the shouldAutorotate methods in both of your viewControllers:
//in blue (landscape only)
-(BOOL)shouldAutorotate{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
return YES;
}
else {
return NO;
}
}
//in red (portrait only)
-(BOOL)shouldAutorotate{
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
//note that UIInterfaceOrientationIsPortrait(self.interfaceOrientation) will return yes for UIInterfaceOrientationPortraitUpsideDown
return YES;
}
else {
return NO;
}
}
Keep the supportedInterfaceOrientations methods the same.
#pragma mark- Orientation Delegate Method:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{ Orientation = [UIApplication sharedApplication].statusBarOrientation;
if (Orientation == UIInterfaceOrientationLandscapeLeft || Orientation == UIInterfaceOrientationLandscapeRight)
{
// self.scrollView.contentOffset = CGPointMake(self.view.bounds.size.width,1200);
[scrollView setScrollEnabled:YES];
[scrollView setContentSize:CGSizeMake(768, 2150)];
}else if (Orientation == UIInterfaceOrientationPortrait || Orientation == UIInterfaceOrientationPortraitUpsideDown)
{
[scrollView setScrollEnabled:YES];
[scrollView setContentSize:CGSizeMake(768, 1750)];
}
}
In order to use navigation with orientation together, you should take a bunch of viewcontrollers like an array.
After that checkout following methods,
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
this changes in your methods will help you a lot.
Enjoy Programming!
I am using UINavigationController in my application and my first controller called A and it is strict to portrite only and there is a button in my A controller.Once I click on the button I am creating instance to the another view controller called B. After creating the instance for B I am presenting modally using below method
[self presentViewController:BInstance animated:YES completion:^{
NSLog(#"completed");
}];
My B controller can able will support all the orientations, this is expected, till the ios5.1 and earlier versions. Now I am trying to run my project on the ios6 using Xcode4.5 it is not getting rotated I am looking forward to solve the issue I found some of the blogs about shouldAutorotateToInterfaceOrientation: method got deprecated from the latest ios6. I used alternative also
-(BOOL)shouldAutorotate{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAll;
}
But still there is not much expected results.
Question: What makes my B controller will work for all orientations even my Parent A works only for portrite.
Thanks In advance All your suggestions/advice useful on this.
First of all, in AppDelegate, write this. THIS IS VERY IMP
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return (UIInterfaceOrientationMaskAll);
}
Then, For UIViewControllers, in which you need only PORTRAIT mode, write these functions
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskPortrait);
}
For UIViewControllers, which require LANDSCAPE too, change masking to All.
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskAllButUpsideDown);
//OR return (UIInterfaceOrientationMaskAll);
}
Now, if you want to do some changes when Orientation changes, then use this function.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
}
I´m having this issue with iOS 6 SDK: I´m having some views that should be allowed to rotate (e.g. a videoview), and some that don´t.
Now I understand I have to check all orientations in the app´s Info.plist and then sort out in each ViewController, what should happen. But it doesn´t work! The app always rotates to the orientations, that are given in the Info.plist.
Info.plist:
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
any ViewController that shouldn´t be allowed to rotate:
//deprecated
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
Observation: App rotates to landscape and portrait orientation. Any ideas why or what I´m doing wrong?
Cheers,
Marc
Edit: My latest findings also indicate, that if you want to have rotation at some point in your app, you have to activate all four rotation directions in your project settings or Info.plist. An alternative to this is to override
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
in your AppDelegate, which overrides the Info.plist. It isn´t possible anymore to set only Portrait in your Info.plist and then having rotation in some ViewController by overriding shouldAutorotateToInterfaceOrientation or supportedInterfaceOrientations.
If your ViewController is a child of a UINavigationController or UITabBarController, then it is the parent that is your problem. You might need to subclass that parent view controller, just overriding those InterfaceOrientation methods as you've shown in your question
EDIT:
Example for portrait only TabBarController
#interface MyTabBarController : UITabBarController
{
}
#end
#implementation MyTabBarController
// put your shouldAutorotateToInterfaceOrientation and other overrides here
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
#end
Adding to CSmith's answer above, the following code in a UINavigationController subclass allows delegation to the top view controller in the way that I expected this to work in the first place:
- (BOOL)shouldAutorotate;
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[self topViewController] respondsToSelector:#selector(supportedInterfaceOrientations)])
return [[self topViewController] supportedInterfaceOrientations];
else
return [super supportedInterfaceOrientations];
}
Here's another alternative to CSmith's approach.
If you want to replicate the pre-iOS 6 behaviour where all the views in the navigation stack / tab bar have to agree on an allowable set of orientations, put this in your subclass of UITabBarController or UINavigationController:
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger orientations = [super supportedInterfaceOrientations];
for (UIViewController *controller in self.viewControllers)
orientations = orientations & [controller supportedInterfaceOrientations];
return orientations;
}
Try to add this category:
#interface UINavigationController(InterfaceOrientation)
#end
#implementation UINavigationController(InterfaceOrientation)
- (NSUInteger) supportedInterfaceOrientations {
if (self.viewControllers.count > 0)
return [[self.viewControllers objectAtIndex:0] supportedInterfaceOrientations];
else
return UIInterfaceOrientationMaskAll;
}
#end
For people using UINavigationController and Swift, you can add this extension to your project. After that, navigation controllers delegate the control to their child controller.
extension UINavigationController {
override public func supportedInterfaceOrientations()
-> UIInterfaceOrientationMask {
if let ctrl = topViewController {
return ctrl.supportedInterfaceOrientations()
}
return super.supportedInterfaceOrientations()
}
override public func shouldAutorotate() -> Bool {
if let ctrl = topViewController {
return ctrl.shouldAutorotate()
}
return super.shouldAutorotate()
}
}
Further addition to #CSmith and #EvanSchoenberg.
If you have some views that rotate, and some views that don't, you must create a custom instance of the UITabBarController, yet still let each UIViewController decide.
- (BOOL)shouldAutorotate;
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
UIViewController * top;
UIViewController * tab = self.selectedViewController;
if([tab isKindOfClass:
([UINavigationController class])]) {
top = [((UINavigationController *)tab)
topViewController];
}
if ([top respondsToSelector:#selector(supportedInterfaceOrientations)])
return [top supportedInterfaceOrientations];
else
return [super supportedInterfaceOrientations];
}
#Alvivi's answer updated for Swift 4.
extension UINavigationController {
// Look for the supportedInterfaceOrientations of the topViewController
// Otherwise, viewController will rotate irrespective of the value returned by the ViewController
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if let ctrl = self.topViewController {
return ctrl.supportedInterfaceOrientations
}
return super.supportedInterfaceOrientations
}
// Look for the shouldAutorotate of the topViewController
// Otherwise, viewController will rotate irrespective of the value returned by the ViewController
override open var shouldAutorotate: Bool {
if let ctrl = self.topViewController {
return ctrl.shouldAutorotate
}
return super.shouldAutorotate
}
}
#Shimanski Artem's answer is good, but I think using the topmost (currently visible) controller is better solution:
#interface UINavigationController(InterfaceOrientation)
#end
#implementation UINavigationController(InterfaceOrientation)
- (NSUInteger) supportedInterfaceOrientations {
if (self.viewControllers.count > 0){
return [[self.viewControllers objectAtIndex:[self.viewControllers count] - 1] supportedInterfaceOrientations];
}
return UIInterfaceOrientationMaskAll;
}
#end
As an alternate option, in case you want to preserve pre-iOS6 rotation functionality in your app:
Here's a helpful bit of code on github that swizzles the method calls for iOS6 so that rotation works like it did on iOS4/iOS4. This really helped me, as I'm supporting a legacy app that really micro-manages its rotations. It would have been a lot of work to implement the changes required for iOS6. Kudos to the user who posted it.
https://gist.github.com/3725118