I understand that this question (or variations of it) has been asked quite a few times, however it's still how to best approach this problem without delving into kludgy hacks.
I have an application with the following layout:
UITabBarController
↳ UINavigationController
↳ PortraitViewController
↳ LandscapeViewController
The first PortraitViewController has its rightBarButtonItem set to a UIBarButtonItem, which calls landscapeButtonPressed:. That method pushes the LandscapeViewController onto the view controller stack.
In LandscapeViewController, I set hidesBottomBarWhenPushed to YES during initialisation, since I only want the navigation bar visible.
I also call setStatusBarOrientation:UIInterfaceOrientationLandscapeRight on [UIApplication sharedApplication] in loadView and viewWillAppear:, and then set it back in to UIInterfaceOrientationPortrait in viewWillDisappear:.
In my shouldAutorotateToInterfaceOrientation: implementation, I return YES only for UIInterfaceOrientationLandscapeRight.
The PortraitViewController looks like this:
Portrait View http://img.skitch.com/20091007-18ur7p3iubkrb1if5cak8wdxkb.png
The LandscapeViewController looks like this:
Broken Landscape View http://img.skitch.com/20091007-f3ki1ga5m4ytkyg3wgwektp86e.png
As you can see, the view is not rotating correctly. If I call the private -[UIDevice setOrientation:] method before I call -[UIApplication setStatusBarOrientation:], I can get the navigation bar to rotate properly, but I'd rather not be calling private methods and there doesn't seem to be a way to get the bounds of my main view for laying out subviews. Using the private method results in this:
Better Landscape View http://img.skitch.com/20091007-8ckbx6gpbiateju9qjgew4x3k2.png
Any ideas on how to solve this problem?
My goal is to have a view in landscape orientation, with valid landscape CGRect coordinates that I can use as the basis for laying out subviews.
Nathan,
I feel that you have set PortraitViewController as the rootViewController of UINavigationController. I also believe that you are restricting the PortraitViewController's orientation only to UIInterfaceOrientationPortrait only in the shouldAutorotateToInterfaceOrientation method. If so then, any view controller that you push will have the orientation of the rootViewController itself unless you are not changing the device orientation by rotating your device.
So if you need the LandscapeViewController in the UIInterfaceOrientationLandscapeRight orientation then just allow this in shouldAutorotateToInterfaceOrientation: method and don't screw things by explicitly setting the device orientation.
The LandscapeViewController is not automatically rotating because interfaceOrientation is UIInterfaceOrientationLandscapeRight, so you have to return YES for UIInterfaceOrientationLandscapeRight
To have your views resizing correctly you have to open your xib file with Interface Builder, then into the Inspector and finally into the tab "Size".
Related
I know how to control the autorotation of a UINavigationController, but I would like to programmatically rotate a UINavigationController: the navigation bar, and view stack, to a specified UIInterfaceOrientation value, regardless of the current device orientation.
Is that possible?
Thanks
First, setup your application/view controller to use only one specific orientation (that is, block automatic rotations).
Then you can rotate the underlaying view layer (UIView.layer of type CALayer) using an affine transform.
Implement the - shouldAutorotateToInterfaceOrientation: in your view controllers insided the UINavigationController, only return YES on your desired orientation. e.g., the UINavigationController only display on landscape:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
May this helps.
I have a child UIViewController with that's part of a hierarchy with a UITabBarController and a UINavigationBarController. Let's call it ChildViewController; then my hierarchy looks like:
UITabBarController
|
UINavigationViewController [tab 1]
|
SomeParentViewController
|
SomeOtherParentViewController
|
ChildViewController
Now I want only ChildViewController to support rotation to landscape orientation. (It's a view controller that shows a chat view, and the landscape mode is easier for typing for some.) I added method - (BOOL) shouldAutorotateToInterfaceOrientation: to ChildViewController to declare that it supports landscape orientation, but rotating the device had no effect. From debugging, I found that – willAnimateRotationToInterfaceOrientation:duration: wasn't being called.
After some searching around online, I've found that a descendent of a UITabBarController only supports a given orientation if the UITabBarController itself supports that orientation. And, strangely enough, UITabBarController only supports an orientation if the view controllers for each of its tabs support rotation. Like tab 1 above, the view controllers for the other three tabs are UINavigationViewController instances; and, because we must go deeper, each UINavigationViewController only supports orientation if its child view controller supports the orientation.
So at this point, adding adding - (BOOL) shouldAutorotateToInterfaceOrientation: to SomeParentViewController and the children of the other UINavigationController instances allowed ChildViewController to rotate. But now SomeParentViewController and the other three tabs will rotate to landscape, and it looks horrible. I only wanted ChildViewController to support landscape.
As a latch ditch effort, I created my own UITabBarController subclass called RotatingUITabBarController and add a global flag to the ChildViewController class that lets me know if it has been created and is displayed. The RotatingUITabBarController overrides only - (BOOL) shouldAutorotateToInterfaceOrientation: and is implemented as:
if ([ChildViewController isDisplayed]) {
return ((toInterfaceOrientation == UIInterfaceOrientationPortrait) ||
(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
return NO;
Now, if I boot the app, switching to SomeParentViewController or any other tab and rotating the phone does not switch to landscape mode, instead keeping in portrait. So far so good. If I create and display ChildViewController and rotate the phone, it enters landscape. So far so good. But now if I pop ChildViewController to reveal SomeOtherParentViewController, it is also in landscape. And so is SomeParentViewController and every other tab that I switch to.
I'm out of tricks now. Any advice would be much appreciated, thanks!
Perhaps the best model for the kind of behavior you seem to want is the YouTube app. Most the interface is portrait-only, but the view that plays videos works in either portrait or landscape.
If you look at that app, you'll notice that the whole tabbed part of the UI is actually a modal view controller. When you launch the app, the tab bar controller is immediately presented modally. The only time you leave that modal tab bar controller is when you play a video -- you'll notice that the whole tabbed interface slides down to reveal the video view. When the video ends, the tab bar controller is again presented modally.
This is an inversion of the "normal" approach, where you use a modal view controller only briefly, but it works very well in the YouTube app. It may or may not work well for you too. The important thing is to make your app predictable and fluid, and make the user feel in control at all times.
I have a UITabBar with 2 bar items. The initial orientation of the device is portrait. If I rotate the device to landscape while being at tabBarItem2 the whole thing(Status Bar, TabBar, ViewContent2) rotates fine, but when I press the tabBarItem1 the ViewContent1 is still in Portrait. It also happens if I'm in tabBarItem1, then rotate device to landscape and I go to tabBarItem2.
I'm using the willRotateToInterfaceOrientation method on each view controller to move things.
I think this is happening because it is triggering the actual viewController's willRotateToInterfaceOrientation method and not on both of them.
Any ideas on how to fix that?
Both view controllers need to have
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Quick problem:
I have an UITabBarController with 2 navigation controllers [lets call them Left and Right Controller]
On the default selected Left Controller I can push a new View Controller that detects interface orientation.
On the Right Controller I can push the same View Controller but it won't detect interface orientation, or for that matter, It won't even go into the shouldAutoRotateInterface method at all T___T
Haaalp!!
If it is of any relevance, the View Contoller that I'm pushing use the hidesBottomBarWhenPushed property.
Most likely this is your problem:
Tab bar controllers support a portrait
orientation by default and do not
rotate to a landscape orientation
unless all of the root view controllers support such an orientation.
When a device orientation
change occurs, the tab bar controller
queries its array of view controllers.
If any one of them does not support
the orientation, the tab bar
controller does not change its
orientation.
The solution is to override the following method on every view controller leading to your view:
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
return YES;
}
For example, instead using the default UITabBarController in IB, replace it with your own subclass containing just the method above.
I'm a bit late to the party on this, but I ran into a problem with autorotation at startup for a tab bar app I wanted always to run in portrait.
The app's plist has the necessary settings to both start in and only allow portrait mode, and all my view controllers only allow portrait mode. Yet, when I started the app holding my iPhone in landscape, the app started in portrait, but then rotated to landscape!
Rather than subclass UITabBarController, I simply overrode UITabBarController's shouldAutorotateToInterfaceOrientation: method using a category on class UITabBarController. I included this code in my app delegate:
#implementation UITabBarController(UITabBarControllerCategory)
-(BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
Works beautifully, and is quite lightweight.
does your uitabbarcontroller implement the auto rotate? any child viewcontroller that wants to implement autorotate has to have its parent implement autorotate.
I am using a UINavigationController to push and pop views. The Navigation bar is hidden.
I have RootViewController, which is set to autorotate. It works just fine when it is visible.
I also have GalleryViewController. It has autorotation disabled by just returning no.
My problem is, when GalleryViewController is on the nav stack, RootViewController stops responding to it's autorotate events.
If I am in Portrait, and I rotate the device (while viewing GalleryViewController) and then tap the back button, it pops the GalleryViewController off the stack and reveals the RootViewController, but RootViewController did not change rotation as it should have.
I stuck in the method -viewWillAppear and checked the status of the UIDevice orientation. It gives me the correct orientation for the device.
BTW, I also tried enabling the autorotate in the GalleryViewController. It then rotates the view correctly but still does not rotate the RootViewController.
It seems that when using the navigation controller, only the top of the stack gets rotated.
QUESTION: Since the device knows it's in the correct rotation when -viewWillAppear is called, can I force it to update somehow??
-mark
instead of returning just YES, return (interfaceOrientaion == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeLeft || and so on...) in the RootViewController.
in the GalleryViewController return only one orientation which in our opinion is the default one.
When thus done - everything should work like you want it.