限制选项卡栏视图控制器的旋转

3

更新2

在我的UITabBarController子类中,我尝试添加了以下内容:

-(void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {

        NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];

}

现在,每当我选择一个选项卡时,设备都会完美地旋转到纵向模式。但是,现在我可以在选择(a)时旋转设备,并且设备将旋转到横向模式。如何停止设备旋转?
我认为我的选项卡控制器子类中的此方法是导致此问题的原因:
- (BOOL)shouldAutorotate {

    if(self.selectedIndex == 0)
    {
        return NO;
    }

    return [self.viewControllers.lastObject shouldAutorotate];
}

如果我返回“NO”,当我在视图控制器中时,我无法旋转,但当我选择它时,它不会自动旋转到纵向。如果我返回“YES”,当我在视图控制器中时,我可以旋转,但当我选择它时,它会自动旋转到纵向。
我的应用程序中有一个自定义选项卡栏控制器,其层次结构如下:
UITabBarController
    |
    UINavigationController
    |  |
    |  UIViewController(a)
    |
    UINavigationController
    |  |
    |  UIViewController(b)
    |
    UINavigationController
       |
       UIViewController(c)

我希望View Controller(a)只能在竖屏模式下查看,而View Controller(b)和(c)可以在所有方向上查看,但不能倒置。现在,我可以单独为每个视图控制器执行此操作,但当我在横向模式下处于(b)中,并选择(a)的选项卡项时,(a)将以横向模式显示,这看起来不好。如何确保选项卡栏(或视图控制器)检查所选视图控制器是否可以在当前方向下查看?
如果需要,这是仅限于竖屏模式的(a)代码:
- (BOOL) shouldAutorotate
{
    return NO;
}

- (NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

更新

我已经将这个添加到视图控制器(a)中,但是我的视图中央出现了一个黑色的正方形,并且导航栏中的标题不再居中。

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];


    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];


}
3个回答

7
自从iOS 7.0版本以来,UITabBarController支持通过- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController进行方向转发。在此之前,它的表现非常出色。
但是,当您处于此特定选项卡不支持的界面方向时,切换到其他选项卡会出现问题。不幸的是,在这种情况下,选项卡控制器不会强制旋转,唯一的方法是使用私有API。
注意:我使用attemptRotationToDeviceOrientation在从仅支持竖屏的选项卡切换回支持任何方向的选项卡时将界面旋转到设备方向。
以下是我的解决方法:
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

我在Github上放了一个例子应用程序,网址为https://github.com/pronebird/TabBarOrientationExample


1
谢谢你的回答,但是这并没有解答我的问题。 - Jacob
@Jacob以哪种方式它没有回答你的问题?你使用了错误的枚举,切换到正确的枚举。 - pronebird
1
我不明白在一个视图控制器中更改返回类型如何影响在选中的标签控制器中的旋转。 - Jacob
@Jacob 如果你将选项卡栏的旋转事件传递给子控制器,那么UIKit将从子控制器中获取所需的方向,而不是从选项卡栏本身获取。其次,你可以尝试在控制器A的viewWillAppear:方法中使用[UIApplication setStatusBarOrientation:animated:]来强制设置特定的方向。 - pronebird
我已按照你的建议更改了枚举类型,但问题仍然存在。我还尝试在视图控制器中手动更改方向(a),但也没有起作用。有什么想法吗? - Jacob
显示剩余9条评论

2
我曾经为这个问题使用了一个变通方法。其想法是通过子类化UITabBarController 并将它的delegate 设置为本身。然后在一个子类中,我进行了以下“hack”:当选择纵向控制器时,我推送并立即关闭一个空的模态VC。这强制iOS以某种方式设置正确的方向。
-(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];
    }
}

请注意,这可能取决于某些实现细节,并且将来可能无法正常工作。
编辑:您还应该在UITabBarController子类中实现supportedInterfaceOrientationsshouldAutorotate。并且您应该在想要保持纵向的视图控制器中的-shouldAutorotate中返回YES。否则,当在选项卡栏中选择时,它将无法从横向旋转到纵向。
- (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;
}

这对我来说不起作用,只有状态栏旋转到纵向位置... - Jacob
可能是因为你在shouldAutorotate中返回了NO。看一下修改后的答案。 - Michał Ciuba
就像我在修改后的问题中所说的,如果在shouldAutorotate中返回YES,我可以在视图控制器被选中时简单地旋转设备,它将会从纵向模式旋转到横向模式。 - Jacob
如果在此视图控制器中实现supportedInterfaceOrientations并返回适当的值,它就不会旋转。至少对我来说是这样的(在将设备旋转到横向后,该视图控制器保持为竖向)。 - Michał Ciuba
在我按照你的建议进行了修改之后,当我旋转设备时它仍然会旋转。 - Jacob

0
简单来说,创建一个UITabBarController的子类,并实现委托方法。
- (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;
    }
}

这样做虽然不能让用户选择选项卡,但是有没有办法我可以检查设备的方向,如果当前方向不受支持,就旋转设备,然后返回是? - Jacob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接