I am adding UIInterfaceOrientation changes to the app. My app is not a tab-based or navigation based. I am adding subviews on top of other.
I went through the document why interface orientation changes not work?
http://developer.apple.com/library/ios/#qa/qa1688/_index.html
I found that the main window controls the interface orientation changes.
But now as my app is not tab-based or navigation-based, is there still any way to achieve it?
P.S. : In my app only launch screens are working properly for interface orientation changes and not the added subviews.
Please help
Thanks in advance.
Use following method to resize subview. OR use autoresizingMask
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if(toInterfaceOrientation == UIInterfaceOrientationPortrait){
viewInformationContainer.frame = CGRectMake(0, 61, 320, 403);// here you can change resize or subviews
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight || toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
viewInformationContainer.frame = CGRectMake(0, 0, 480, 227);//here you can change resize or subviews
}
}
Hope, this will help you...
Reading the comments posted above and the problem that you are facing what I can say is that You have added a hierarchy of subviews. Consider the following Case
1) I add a view controller (VC) , Navigation Controller , Tab bar controller directly to the window.. the subsequent views will get notify about any orientation changes properly.
2) I add a Parent view Controller to the window and later add views of some other view controllers to my parent view controller, in this particular case you will get the orientation change delegate methods being called properly in the Parent VC but not in the subsequent view controllers whose view you have added to your Parent VC
I once added a Base view controller to my application window where base controller had a ScrollView , I later added subviews of my four view controllers to scrollview.
(void)viewDidLoad {
DNAppDelegate *appDelegate = (DNAppDelegate*)[UIApplication sharedApplication].delegate;
CGRect viewCustormFrame = appDelegate.viewCustomTabBar.frame;
viewCustormFrame.origin.y = self.view.frame.size.height-35;
if(UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) {
viewCustormFrame.size.width = self.view.frame.size.width;
}
else {
viewCustormFrame.size.width = self.view.frame.size.width;
}
[self.view addSubview:appDelegate.viewCustomTabBar];
appDelegate.viewCustomTabBar.frame = viewCustormFrame;
[srclViewMainContent addSubview:appDelegate.homeController.view];
[srclViewMainContent addSubview:appDelegate.newsListController.view];
[srclViewMainContent addSubview:appDelegate.columnController.view];
[srclViewMainContent addSubview:appDelegate.whatsOnController.view];
currentVisisbleController = appDelegate.homeController;
[self resetAllSizes];
[super viewDidLoad];
}
In above case though i got all orientation delegate methods being called in base VC but not in the other Four VC.. so i had to tweak something like
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
DNAppDelegate *appDelegate = (DNAppDelegate*)[UIApplication sharedApplication].delegate;
[appDelegate.homeController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
[appDelegate.newsListController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
[appDelegate.columnController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
[appDelegate.whatsOnController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
DNAppDelegate *appDelegate = (DNAppDelegate*)[UIApplication sharedApplication].delegate;
currentPageOffset = srclViewMainContent.contentOffset.x/srclViewMainContent.frame.size.width;
[appDelegate.homeController willRotateToInterfaceOrientation:toInterfaceOrientation duration:(NSTimeInterval)duration];
[appDelegate.newsListController willRotateToInterfaceOrientation:toInterfaceOrientation duration:(NSTimeInterval)duration];
[appDelegate.columnController willRotateToInterfaceOrientation:toInterfaceOrientation duration:(NSTimeInterval)duration];
[appDelegate.whatsOnController willRotateToInterfaceOrientation:toInterfaceOrientation duration:(NSTimeInterval)duration];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
DNAppDelegate *appDelegate = (DNAppDelegate*)[UIApplication sharedApplication].delegate;
[self resetAllSizes];
CGRect visibleRect = CGRectMake(currentPageOffset*srclViewMainContent.frame.size.width, 0, srclViewMainContent.frame.size.width, srclViewMainContent.frame.size.height);
[srclViewMainContent scrollRectToVisible:visibleRect animated:NO];
[appDelegate.homeController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[appDelegate.newsListController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[appDelegate.columnController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[appDelegate.whatsOnController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
i.e call these delegate methods on respective VCs from the Base VC. I had to do this because my application was completely build and later I had to change the architecture to allow scrolling among various views, But I had already implemented these views by taking four View controllers.
In a nutshell any VC (also Navigation Controller, tab Bar controller, split View controller)added directly to window will propagate these Orientation change delegate methods to subsequent VCs but a View controller will not pass these delegate methods to VCs coming down the lane
I hope this gives you some insight. Just in case if you come across any good solution do post in as an answer and share with us all.
Related
When the UIDocumentInteractionController is dismissed, the presenting view controller's views are removed, including elements from the UINavigationController.
The UIDocumentInteractionController dismisses and the presenting view controller's views are removed, leaving a plain white/gray box where the presenting view controller formerly existed. The app no longer responds to any touch events after this point.
This Occurs on iPad Simulator (iOS 7.0) and iPad 3 (Wifi) running iOS 7 for Quick Look Pdf Reader.
Does not matter whether the application was compiled against the iOS 6.1 or iOS 7 SDK
Please let me know your suggestions.
I have this same problem when presenting a UIDocumentInteractionController from a view controller presented as a modal form sheet on iPad in iOS 7 (worked fine in iOS 6).
It looks like that during the transition from the document interaction controller back to the presenting view controller, the presenting view controller's view is wrapped in a temporary UITransitionView, and then that transition view is being removed from the view hierarchy after the transition is complete, along with the presenting view controller's view, leaving just UIDropShadowView that is the backing view of the modal form sheet visible (the gray box).
I worked around the problem by keeping a reference to my presenting view controller's root view (the one just before the drop shadow view in the hierarchy) when the document interaction controller preview will begin, and restoring that view to the hierarchy when the document interaction controller preview has ended.
Here's sample code:
- (void)documentInteractionControllerWillBeginPreview:(__unused UIDocumentInteractionController *)controller {
if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
// work around iOS 7 bug on ipad
self.parentView = [[[self.view superview] superview] superview];
self.containerView = [self.parentView superview];
if (![[self.containerView superview] isKindOfClass: [UIWindow class]]) {
// our assumption about the view hierarchy is broken, abort
self.containerView = nil;
self.parentView = nil;
}
}
}
- (void)documentInteractionControllerDidEndPreview:(__unused UIDocumentInteractionController *)controller {
if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
if (!self.view.window && self.containerView) {
assert(self.parentView);
CGRect frame = self.parentView.frame;
frame.origin = CGPointZero;
self.parentView.frame = frame;
[self.containerView addSubview: self.parentView];
self.containerView = nil;
self.parentView = nil;
}
}
}
I found Michael Kuntscher answer to be right on target. A slight modification is necessary if the UIDocumentInteractionController is presented from a popover.
There is a slight dependence on the view hierarchy that can be eliminated by iterating over the parent views until one with a UIWindow superview is found. In addition, I found that when a document interaction controller is presented from within a popover it is slightly different views that need to be stored as the parentView and containerView (specifically we want to find a containerView such that its superview is a UIPopoverView). The following snippet is a re-worked version of Michael's answer to incorporate these changes (note that UIPopoverView is a private class, so we use string representations of the class rather than making a direct reference to each class):
- (void)documentInteractionControllerWillBeginPreview:(UIDocumentInteractionController *)controller {
/* iOS 7 DIC bug workaround */
if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
UIView *a_view = self.view;
self.dicParentView = nil;
self.dicContainerView = nil;
while (a_view != nil) {
UIView *super_super_view = [[a_view superview] superview];
NSString *str_class = NSStringFromClass([super_super_view class]);
if ([str_class isEqualToString:#"UIWindow"] ||
[str_class hasSuffix:#"PopoverView"]) {
self.dicParentView = a_view;
self.dicContainerView = [a_view superview];
break;
}
a_view = [a_view superview];
}
if (self.dicParentView == nil) {
NSLog(#"Could not appropriate superview, unable to workaround DIC bug");
}
}
/* end work around */
}
- (void)documentInteractionControllerDidEndPreview:(__unused UIDocumentInteractionController *)controller {
/* iOS 7 DIC bug workaround */
if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
if ((self.view.window == nil) &&
(self.dicContainerView != nil) &&
(self.dicParentView != nil)) {
NSLog(#"Restoring view for DIC presenter in the view hierarchy");
CGRect frame = self.dicParentView.frame;
frame.origin = CGPointZero;
self.dicParentView.frame = frame;
[self.dicContainerView addSubview: self.dicParentView];
self.dicContainerView = nil;
self.dicParentView = nil;
}
}
/* end work around */
}
I had the exact same problem with the app stalling on a grey form sheet modal view that had lost all of its content after a UIDocumentInteractionController had been presented and dismissed. The two solutions here are great but I simplified them to cater for my particular case, which was a UINavigationController inside a form sheet modal that can present a PDF in the UIDocumentInteractionController, which I wanted to be full screen modal instead of pushed in the navigation controller because the form sheet area is too small for the PDF to be easily readable.
I implemented two UIDocumentInteractionControllerDelegate methods. Assume the following:
self.navController is a reference to the UINavigationController that is presented inside the form sheet modal.
there is a member variable declared in the UIViewController subclass #property (nonatomic, strong) UIView* docInteractionControllerWorkaroundSuperview;
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO is a #define for ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
Firstly:
-(UIViewController*)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController*)controller
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0") && self.navigationController.modalPresentationStyle == UIModalPresentationFormSheet)
{
self.docInteractionControllerWorkaroundSuperview = [self.navigationController.view superview];
}
return self.navigationController.visibleViewController;
}
then:
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
if (self.docInteractionControllerWorkaroundSuperview != nil)
{
NSLog(#"Workaround iOS 7 document interaction bug... resetting nav controller view into modal");
//reset the nav controller view from whence it came.
self.navigationController.view.frame = CGRectMake(0.0, 0.0, self.navigationController.view.frame.size.width, self.navigationController.view.frame.size.height);
[self.docInteractionControllerWorkaroundSuperview addSubview:self.navigationController.view];
self.docInteractionControllerWorkaroundSuperview = nil;
}
}
i.e. when presenting the UIDocumentInteractionController, look at the superview of the navigation controller's view. It works out to be a UIDropShadowView which I assume is the partially transparent grey/black background to the form sheet modal that dims out the view behind that presented the modal.
When the PDF has been dismissed, in documentInteractionControllerDidEndPreview the superview of the navigation controller's view is now a UITransistionView. But then shortly after that (when the transition has completed), it gets set to nil. Somehow it got detached (or didn't get re-attached) from the UIDropShadowView. By keeping a reference to view this when presenting the UIDocumentInteractionController, we can reattach it manually and everything works fine. Then be sure to nil out the reference to make sure we don't accidentally retain it.
This method does not affect the behaviour on iOS 6 or any previous iOS versions.
I am working on a ViewController which switches view based on a UISegmentControl. I found a tutorial online after viewing this question on stack overflow
The main view controller is a tab view controller and then on one of the tabs there is a uinavigationcontroller which contains a segmented control (hope your still with me).
The problem I am having is with the height of the view of the views in the segmented control. The first selected view is resized correctly but all other views within the segment control ignore the fact there is a tab bar there and there views go under the tab bar.
I have tried adding the following but it doesn't seem to help.
controller1.view.autoresizesSubviews = YES;
controller1.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
controller2.view.autoresizesSubviews = YES;
controller2.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Any ideas?
Another issue is with rotation. If I rotate when on the segmented view. The current selected view will rotate, if I change segment the view will have not rotated to landscape and will only take up a small proportion of the screen (as shown below).
I have overwrote all the rotation related methods in the SegmentedViewController and looped over the array of views it is managing and called there method. This doesn't seem to have done the job unfortunately.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
for (UIViewController * viewController in self.segmentedViewControllers)
[viewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
for (UIViewController * viewController in self.segmentedViewControllers)
[viewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
for (UIViewController * viewController in self.segmentedViewControllers)
[viewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
etc…
Managed to solve this issue by implementing it a different way as shown here http://redartisan.com/2010/6/27/uisegmented-control-view-switching-revisited
I am working on an app (my first one), which is basically a TabBar app.
To be more precise there are:
- a login view controller
- a tab bar controller (when login is done)
- a landscape view controller that is used when the first itel of the TabBar is switch from Portrait to Landscape.
So, when I am in the first tab, I need to be able to move to landscape view to display some other data. In my tab bar controller, I have implemented those methods:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if([self selectedIndex] == 0)
return YES;
return NO;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Get AppDelegate
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
// Remove TabBarView and add graph Landscape View
[self.view removeFromSuperview];
[delegate setSubViewLandscapeViewController];
}
}
In the delegate, I have implemented the setSubViewLandscapeViewController and the setSubViewTabBarController:
- (void)setSubViewTabBarViewController {
[window addSubview:[tabBarController view]];
}
- (void)setSubViewGraphLandscapeViewController {
[window addSubview:[landscapeViewController view]];
}
I want the landscapeViewController to display only in landscape mode, I have then (in my landscapeViewController):
- (void)viewWillAppear:(BOOL)animated {
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
NSLog(#"willRotateToInterfaceOrientation");
}
A part of this works fine, I mean the switch from portrait to landscape is ok (when I am in the first tab), the tabbarcontroller is remove from the SuperView and the landscape view is added as a subview instead.
The thing is... I do not know how to switch back to portrait mode (and then load the previous controller, the tabBar one using the setSubViewTabBarViewController of my delegate). It seems none of the willRotateToOrientation, willRotateFromOrientation, .. are triggered when I actually move the device from the landscape view...
In short, when I am in the landscape view I do not know what to do to move back to the tabbar view... I am kind of stuck in the landscape view once I am in this one.
Thanks a lot for your help,
Luc
Look at the pie chart in CPTestApp-iPhone in the examples folder. It handles rotation by implementing -(void)didRotateFromInterfaceOrientation: and resizing the graph after a rotation.
Well, I managed to get a solution for this problem.
In fact, while moving from portrait to landscape I removed the tabbarcontroller from window subview and add the landscapeviewcontroller instead.
It seems it was not the correct thing to do.
Instead, I add the landscapeViewController as subview of the tabbarcontroller and remove it when going from landscape to portrait.
I still have a problem however with the y position of the landscape view which seems to changes when I do several decive rotation in a row....
Regards,
Luc
My app has about 10 different UIViewControllers, just one of which I want to switch to landscape mode if the device is rotated. (All the rest, I want to keep in portrait.)
In order to implement rotation on that one view, I needed to implement its controller's 'shouldAutorotate' method and return YES. Since this view is accessed via a navigation controller, I also needed to create a subclass of UINavigationController that implements 'shouldAutorotate' and return YES.
This solution works, but too well. I find that all of the UIViewControllers I push onto my subclass of UINavigationController respond to rotation, even if I implement 'shouldAutorotate' and return NO. (Remember: I only want one particular UIViewController to respond to rotation, not every one in the navigation controller's stack.)
So, my question is: how do I best do this? All the solutions I can come up with seem 1) cumbersome, and 2) worse, don't seem to work.
Thanks very much.
when you implement UINavigationController, this class is your the parent that controls all the children viewControllers that will be pushed into the stack. Therefore, the RootViewController is the only controller that say Yes or No to autorotation. Even if, you are passing Yes to auto-rotation in children view controllers, they don't count!
This is the nature of UINavigationController. So to change it, you have two choices:
1- Manually manipulate it, which requires you to go through some cumbersome codes.
2- Change your design so that it is more compatible to UINavigationController. That single view that should rotate, should be get called by the RootViewController (not the Navigation Root View Controller -- they are named the same, but are totally different), the view that hosts NavController. And when the device rotates, it will either push the NavController to the view or the other.
3- The other method, which will also work, but is not recommended because it violates the concept of MVC, is that your NavController can listen to Notifications. That particular child view that CAN and SHOULD rotate can cast a notification -- e.g. rotateMe, and once the NavController hears it, it rotates.
As I said, it will work, but it violates the MVC model -- which is fine to Apple, but from programming perspective is not recommended.
If you need further explanation about either of them, please let me know.
I do this by having a root view controller (this could be a UITabBarController) and in it's viewDidLoad method i subscribe to rotation events:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(didRotate:)
name:#"UIDeviceOrientationDidChangeNotification"
object:nil];
Then in the didRotate: method i look at which view controller is visible when the rotation happened, and what orientation the phone is in:
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
/*
DEVICE JUST ROTATED TO PORTRAIT MODE
orientation == UIDeviceOrientationFaceUp ||
orientation == UIDeviceOrientationFaceDown
*/
if(orientation == UIDeviceOrientationPortrait) {
/*
DEVICE JUST ROTATED TO LANDSCAPE MODE
*/
}else if(orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight) {
}
}
Within that didRotate: you can look at which is the visible viewController and do what you want from there.
I present a modal view controller in landscape mode when a particular view controller is visible and the phone is rotated into landscape. If any other view controller is visible, i ignore the event.
I force my modal view controller to display in landscape mode in its viewWillAppear method - i can give anyone this code if they want it.
Hope this helps.
Dave
Returns YES on the navigation controller subclass only if [[navController topViewController] isKindOfClass:[RotatingViewController class]]
I have seen examples using the way you are doing it but couldn't get it to work properly. I found the better way to do it from Apples examples. Basically you implement a presentModalViewController and create another view. UIKit does a basic rotation animation and fades between the view. You have to implement the rotated view as a delegate class so it can call back to its calling class to dismiss it and update the orientation.
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
NSLog(#"orientationChanged");
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
And then to display a landscape screen:
- (void)updateLandscapeView
{
PortraitView *portraitView = [[PortraitView alloc] init];
portraitView.delegate = self;
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController: portraitView animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
[portraitView release];
}
I have a UITabBarController, and each tab handles a different UIViewController that pushes on the stack new controllers as needed. In two of these tabs I need, when a specific controller is reached, the ability to rotate the iPhone and visualize a view in landscape mode. After struggling a lot I have found that it is mandatory subclassing UITabBarController to override shouldAutorotateToInterfaceOrientation. However, if i simply return YES in the implementation, the following undesirable side effect arises:
every controller in every tab is automatically put in landscape mode when rotating the iPhone.
Even overriding shouldAutorotateToInterfaceOrientation in each controller to return NO does not work: when the iPhone is rotated, the controller is put in landscape mode.
I implemented shouldAutorotateToInterfaceOrientation as follows in the subclassed UITabBarController:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if([self selectedIndex] == 0 || [self selectedIndex] == 3)
return YES;
return NO;
}
So that only the two tabs I am interested in actually get support for landscape mode.
Is there a way to support landscape mode for a specific controller on the stack of a particular tab?
I tried, without success, something like
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if([self selectedIndex] == 0 || [self selectedIndex] == 3)
{
if ([[self selectedViewController] isKindOfClass: [landscapeModeViewController class]])
return YES;
}
return NO;
}
Also, I tried using the delegate method didSelectViewController, without success.
Any help is greatly appreciated.
Thank you.
Here's an extension to UITabBarController that delegates calls to shouldAutorotateToInterfaceOrientation to the currently selected child controller. Using this extension, you don't need to subclass UITabBarController anymore and you can use shouldAutorotateToInterfaceOrientation in your controllers like one would expect.
UITabBarController+Autorotate.h:
#import <UIKit/UIKit.h>
#interface UITabBarController (Autorotate)
#end
UITabBarController+Autorotate.m:
#import "UITabBarController+Autorotate.h"
#implementation UITabBarController (Autorotate)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
controller = [(UINavigationController *)controller visibleViewController];
return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#end
This worked for me:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(self.selectedIndex == 0 && [[[self.viewControllers objectAtIndex:0] visibleViewController] isKindOfClass:[MyViewController class]])
return YES;
else
return NO;
}
I've been able to use this for a while now (from my app's tab bar controller) without problems:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
That way, in the appropriate VC, we get to do the real check, in this case for a photo gallery view (what else?):
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
My gallery view isn't even at the top-of-stack for a given Nav Controller. It still gets called.
Alas, I just discovered that this doesn't work so well when the VC is lurking within the MoreViewController (as opposed to the four main tabs). In that case, my gallery VC never gets called. I think it's because the VC I've been calling all along is really the nav controller from the selected tab, which then propagates things to the appropriate VC, in this case my photo gallery VC. But for the More VC, things don't work so nicely ... aaaand things go rotationally downhill from there. :\
I tried using the modifications by Andreas (see elsewhere in this thread), to no avail. Clues welcome!
I ran into the same issues as you did when working with the UITabBarController. I needed to control which UIViewControllers were allowed to rotate and which were not. My main problem was with the MORE tab. I did not want any of the UIViewControllers included in the MORE tab to rotate.
My solution was to create my own UITabBarController which I called MyTabBarController:
#interface MyTabBarController : UITabBarController <UITabBarDelegate> {
}
Then I implemented the shouldAutorotateToInterfaceOrientation method:
#implementation MyTabBarController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = [self selectedViewController];
if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4))
{
return interfaceOrientation == UIInterfaceOrientationPortrait;
}
return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#end
I needed to discover if the MORE tab was selected. This is a two step process; when the MORE tab is selected initially the API returns a selectedIndex higher than 4 so I needed to compare the selected controller with the moreNavigationController.
If an UIViewController is selected from within the MORE tab then the selectedIndex is finally 4 but the selectedController is not the moreNavigationController anymore but the UIViewController selected.
The if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4)) takes care of this issue.
Now, when I run my application my UIViewControllers in the MORE tab are not rotated. I hope this will help other developers who are running into the same issues as I did.
Emilio
From what I have seen here and elsewhere I have stitched together a solution that uses the method shouldAutorotate since the old shouldAutorotateToInterfaceOrientation has been deprecated.
I have placed it inside a category to UITabBarController. I so hope this is admissible!
// call to method shouldAutorotate replaces call to method shouldAutorotateToInterfaceOrientation (deprecated)
-(BOOL)shouldAutorotate
{ // check whether selected view controller should autorotate
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
{ // in case it is a navigation controller: get visible view of that
controller = [(UINavigationController *)controller visibleViewController];
}
return [controller shouldAutorotate];
}
Thank you, Thank you, Thank you. This has been 2 days in figuring out how to do this. Here is my take on all of your great help when you have a tabBarController with navigationControllers.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
controller = [(UINavigationController *)controller visibleViewController];
if([controller isKindOfClass:[LOCviewcontroller class]])
return YES;
else
if([controller isKindOfClass:[personWebSiteView class]])
return YES;
else return NO;
}
Any critique of a neophite coder's code is always appreciated...jack
Is it really OK to subclass UITabBarController (as suggested in the accepted answer above)?
I've understood that Apples says something like "you should never ever subclass UITabBarController or UINavigationController" - or have I misunderstood?
Anyway; I found this tutorial where they subclasses a UIViewController in which they put a UITabBarController.