Force rotation of a UIViewController in a NavigationController in a TabbarController - objective-c

I've got my View hierarchy set up like this.
UITabBarController
|
UINavigationController
| |
| UIViewController
|
UINavigationController
|
UIViewController
The thing is that I have a ViewController that is only shown in Portrait, as soon as I turn the device to landscape another ViewController is being pushed on top, that is working as intended so far.
What I want now is that as soon as I push the back button on the newly popped ViewController that the old ViewController is being forced to Portrait, even though the Device is still in Landscape.
I have tried it with transitions, but then the other views are getting screwed and don't properly recognize there orientation anymore leading to a mess of displaced views.

The only thing that worked for me was using
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
in my ViewController and presenting it with presentModalViewController.
This forces the View to stay in landscape.
Not exactly what I wanted, but it worked out.

Try adding this to your original view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) return YES;
return NO;
}
This should force it to only display in portrait orientation.

The code below will force the view into landscape mode, but only if the parent view controller supports landscape mode.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
If the parent view controller of the view you want to force into landscape mode is portrait only, you'll have to create a new new navigation controller. The easiest way to do that is just presenting the forced-landscape view controller in a modal view. So use presentModalViewController instead of pushViewController.

Related

Fixed orientation for a view controller

In my app I've a UITabBarController that contains 3 UINavigationController. I can freeze orientation for any UIViewController. For this I've implemented UITabBarController and UINavigationController. But problem occurs when I push a UIViewController from another one that was in Landscape. The newly pushed UIViewController should be in portrait but it remains in Landscape. I know problem may be unclear so I am attaching scrrenshots.
Lets consider its VC-A.
Pressing edit from VC-A goes to this VC-B. VC-B is:
This VC-B should always in portrait. VC-A can be in Landscape or portrait. When VC-A is in landscape and I press edit the VC-B is pushed and VC-B comes in landscape mode though I implemented these methods in VC-B:
-(BOOL)shouldAutorotate {
return UIInterfaceOrientationMaskPortrait;
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
Your shouldAutorotate needs to return a BOOL not the mask, so code should be
-(BOOL)shouldAutorotate {
return YES;
}
All child view controllers in a tab bar controller or navigation controller must have the same set of supported orientations. This is discussed near the bottom of the following document.
https://developer.apple.com/library/ios/qa/qa1688/_index.html

launching a modal view controller to work around the orientation "lock" of tab bar and nav VCs?

I have an iPhone app with a root view controller (VC) of UITabBarController (set to portrait orientation) with several tabs, one of which is a simple UIViewController. In that UIViewController is a single button - "Play Video", which, when clicked opens a modal view of the video (and automatically starts playing the video). The video view is a UIWebView in a UIViewController. I've been trying to get the Web View's VC to change orientation to landscape but have not had any luck.
I've looked around and understand that if you have a Tab Bar or a Nav controller, all children VCs will be the same orientation as the parent - makes sense. This is why I made the web view's VC modal, hoping this is a way around the orientation issue.
My question is: is this accurate - that using modal will not require the web view VC to be portrait and can respond to the shouldAutorotateToInterfaceOrientation method (even though I have not yet been able to get it to work)?
BTW, using iOS 6.
Thanks in advance.
Apparently in ios6 and above, the way rotation works is different. So what you have to do is the following
In your .plist support all 4 orientations.
Subclass the UITabBarController (for e.g: CustomTabBarController)
In the CustomTabBarController put the following lines of code
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
In your app delegate or where ever you are initializing UITabBarController, replace those instances with CustomTabBarController instances.
In your modal controller put the lines
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
-(BOOL)shouldAutorotate{
return NO;
}
And it should all work.
Apparently the trick, I found is that, UITabBarController will not listen to your instructions. It will support all the orientations you mention in the .plist.
There fore you have to subclass it.
I tried doing all of the above and it works fine. Do let me know and I can send you the code if you want.
Try this. Just have portrait set in the summary screen, then in the app delegate, implement this:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
In the tab bar controller (and no other rotation code):
-(BOOL)shouldAutorotate {
return NO;
}
And finally, in the modal view controller:
-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}

iOS 6 UIInterfacePortrait ONLY viewcontroller being presented & stuck in landscape... when coming back from a landscape viewcontroller in nav stack

So like many others, I ran into the problem of only having one or two viewcontrollers support both portrait and landscape interface orientations, in an otherwise portrait only app. Everything worked fine prior to iOS 6, but suddenly autorotating stopped working. Thanks to some great questions here, I was able to resolve that issue by having the initial navController return the individual topViewController's preference for shouldAutorotate via:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
However, I have stumbled onto a new problem. the root vc (viewController A) should NOT autorotate and should only support portrait. ViewController B in the nav stack supports portrait and landscape. If I am in viewController B, and am in landscape, and touch "back" to pop the view back to viewController A... vc A loads in landscape, which it is not supposed to even support, and wont rotate back to portrait because shouldAutorotate for vc A is set to NO...
Any ideas on how to deal with this would be greatly appreciated. My initial thought was to override vc B's "back" button with a manual method that first force rotates back to portrait if the view is in landscape... then pops the viewcontroller back to vc A... but I cant figure out how to force a rotation programatically. Any ideas?
here are the interface methods in vc A:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
and here is what they are in vc B:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
In vcA set
-(BOOL)shouldAutorotate
{
return YES;
}
But keep
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
Then the view will rotate back to the (only) supported orientation when you return from vcB
The problem is that all container view controllers (Tab Bar Controller, Navigation Controller etc.) support all those interface orientations that you give in your plist file. When the system asks for the supported interface orientations the root view controller's settings and method implementations override it's children's.
In this case the Navigation Controller supports both landscape and portrait and when the B View controller pops, although the system asks for A's interface orientations, it will ask it's root view controller too and that will be the "winner" and since the Navigation Controller supports landscape, it stays in landscape despite that A supports portrait only.
One solution is, that you subclass the root view controller and change it's rotation methods dynamically as needed. When only portait is needed your root's implementation should return portait only and when both orientations are available, then your root should return both.

Landscape mode with modal view

I am trying to add a modal view to my ipad app. All views are supposed to be in landscape mode.
For style I chose form or a page sheet.
Here is the problem. When I add modal view to my view with the following code:
TempController *tmpViewController = [[TempController alloc] initWithNibName:#"TempView" bundle:nil];
tmpViewController.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController:tmpViewController animated:YES];
My modal view is displayed in landscape mode, but the view below it is displayed in Portrait. After modal gets dismissed, view is still in potrait. If I don't attach modal to the view, the view is displayed fine, in landscape mode.
I played with statusBarOrientation and shouldAutootateToInterfaceOrientation, but still no luck. I am running xcode 4.4.1 on Mountain Lion
update: this is my shouldAutorotateToInterfaceOrientation:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
return YES;
}
return NO;
}
A quick note: presentModalViewController:animated: is deprecated. Apple recommend using presentViewController:animated:completion:.
To make sure I understand the question, the view controller that presents the modal view controller displays correctly in landscape mode, but then as soon as it presents the modal it changes itself to portrait mode, even though the modal view controller also displays correctly in landscape? Is this happening with an iPhone/iPod touch or iPad? What does your code look like for the shouldAutoRotateToInterfaceOrientation method of the presenting view controller?

iOS app - changing the orientation in tabbar

In my app I have a tabbar with 5 buttons. The total app has only portrait orientation and I have set it correctly.
My problem is that when I click the second button of the tabbar, the view appears normally in portrait but when the user tilts the device I want that particular view to be get changed to landscape. Is it possible to change the tabbar position to landscape and when the other buttons are clicked all to be changed to portrait?
Else while it is tilted I have to show the view alone in landscape without the tabbar, how can I do this?
So, to be clear, you want the entire app and all views in portrait EXCEPT for the view triggered by the second tab bar button, which you want to always appear in Landscape?
if I've not confused your question, than just put the following line of code in the "viewDidLoad" method of the controller of view number 2
self.view.transform = CGAffineTransformMakeRotation(M_PI / 2);
You probably want to bite the bullet and let your project accept multiple orientations at its highest level and then decide per UIViewController which orientations are supported. This will allow the frameworks to take care of a lot of the work for you (always a good idea).
In each view's controller, override this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
You will want to return YES for any orientation that you want the view to support.
The four options for the interfaceOrientation parameter are:
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
So for your case, you will want the view controller hosting your view and tab bar to support all of the orientations. This is how the function would look:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return YES;
}
But for a extra detail, if you wanted to support only Landscape Left and Portrait Upside Down orientation, the function would look like this:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
A UITabBar only supports rotation, if the ViewControllers of all tabs return YES at shouldAutorotateToInterfaceOrientation.
You could achieve what you want, if you let your "portrait" tabs check if they are visible (via
tabbar.selectedViewController
or
tabbar.selectedIndex
and only reply yes when they're not selected.
The user experience could be confusing though, when the user changes the tab in landscape mode
a portrait view is presented ...
try this w.r.t.: tabbar.selectedViewController
[[UIDevice currentDevice] setOrientation:UIDeviceOrientationLandscapeRight];