Reputation: 2338
UPDATE 2
In my UITabBarController subclass I tried adding this:
-(void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
Now, everytime I select a tab item, the device rotates to portrait mode perfectly. However, now I can rotate the device while (a) is selected, and the device will rotate to landscape mode. How can I stop the device from rotating?
I believe this method in my tab controller subclass is what is causing this:
- (BOOL)shouldAutorotate {
if(self.selectedIndex == 0)
{
return NO;
}
return [self.viewControllers.lastObject shouldAutorotate];
}
If I return 'NO' I cannot rotate when I'm in the view controller, but when I select it, it does not automatically rotate to portrait. If I return 'YES' I can rotate when I'm in the view controller, but when I select it, it automatically rotates to portrait.
I have a custom Tab Bar Controller in my app with the following hierarchy:
UITabBarController
|
UINavigationController
| |
| UIViewController(a)
|
UINavigationController
| |
| UIViewController(b)
|
UINavigationController
|
UIViewController(c)
I want View Controller (a) to only be able to be viewed in portrait mode, and View Controllers (b) and (c) to be viewable in all orientations but upside down. Now, I can do this with each view controller individually, but my issue comes in when I am in (b) in landscape mode, and select the tab item for (a), a is then displayed in landscape mode, which does not look good. How can I make sure the tab bar (or the view controller) checks to see if the to-be-selected view controller can be viewed in the current orientation?
If needed, here is my code for (a) that restricts it to portrait mode on its own:
- (BOOL) shouldAutorotate
{
return NO;
}
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
UPDATE
I have added this to view controller (a), but I get a black square in the middle of my view, and the title in the nav bar is no longer centered.
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}
Upvotes: 3
Views: 5063
Reputation: 12260
Since iOS 7.0 UITabBarController supports orientation forwarding via - (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
.
It works great until you switch to the other tab while being at interface orientation that this particular tab does not support.
Unfortunately tabbar controller does not force the rotation in that case and the only way is to use private API.
Note: I use attemptRotationToDeviceOrientation
to rotate interface to device orientation when switching back from portrait only tab to tab that supports any orientation.
This is my approach to problem:
static const NSInteger kPortraitOnlyTabIndex = 1;
@interface TabBarController : UITabBarController<UITabBarControllerDelegate>
@end
@implementation TabBarController
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
self.delegate = self;
}
return self;
}
- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController {
if(tabBarController.selectedIndex == kPortraitOnlyTabIndex) {
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
[UIViewController attemptRotationToDeviceOrientation];
if([self.viewControllers indexOfObject:viewController] == kPortraitOnlyTabIndex) {
SEL sel = NSSelectorFromString(@"setOrientation:");
if([[UIDevice currentDevice] respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[[UIDevice currentDevice] performSelector:sel withObject:(__bridge id)((void*)UIInterfaceOrientationPortrait)];
#pragma clang diagnostic pop
}
}
}
@end
I put example app on Github at https://github.com/pronebird/TabBarOrientationExample
Upvotes: 7
Reputation: 7944
I used a workaround once for this exact problem. The idea was to subclass UITabBarController
and set its delegate
to itself. Then in a subclass I did a following "hack": when the portrait-only controller was selected, I pushed and immediately dismissed an empty modal VC. It forced iOS to set the correct orientation somehow.
-(void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
NSInteger currentViewControllerSupportsLandscape = ([viewController supportedInterfaceOrientations] & UIInterfaceOrientationMaskLandscape);
if(UIInterfaceOrientationIsLandscape(currentOrientation) && !currentViewControllerSupportsLandscape) {
//workaround to force rotating to portrait
UIViewController *c = [[UIViewController alloc]init];
[viewController presentViewController:c animated:NO completion:nil];
[viewController dismissViewControllerAnimated:NO completion:nil];
}
}
Note that it's probably dependent on some implementation detail and can possibly stop working in the future.
EDIT: You should also implement supportedInterfaceOrientations
and shouldAutorotate
in a UITabBarController
subclass. And you should return YES
in -shouldAutorotate
in the view controller you want to stay in portrait. Otherwise it won't rotate from landscape to portrait when selected in tab bar.
- (NSUInteger) supportedInterfaceOrientations {
return [self.currentViewController supportedInterfaceOrientations];
}
- (BOOL) shouldAutorotate {
return [self.currentViewController shouldAutorotate];
}
- (UIViewController*) currentViewController {
UIViewController* controller = self.selectedViewController;
if([controller isKindOfClass:[UINavigationController class]]) {
controller = [((UINavigationController*)controller).viewControllers objectAtIndex:0];
}
return controller;
}
Upvotes: 2
Reputation: 1702
Its simple, Create subclass of UITabBarController, and implement delegate method
- (BOOL)tabBarController:(UITabBarController *)tbController shouldSelectViewController:(UIViewController *)viewController {
//Check for orientation here
if (supported orientation for view controller == [[UIApplication sharedApplication] statusBarOrientation]) {
return YES;
} else {
return NO;
}
}
Upvotes: 0