如何在仅支持竖屏的应用程序中以横屏或竖屏方式呈现视图控制器

10

我有一个iPhone应用程序,其中大部分都是纵向显示的,但是有一个视图控制器(视频播放器屏幕)必须支持纵向和横向两种方式(仅适用于iOS 8)。为了实现这一点,我已经将应用程序的plist设置为支持纵向和两种类型的横向,然后继承了UINavigationController;在这个子类中,我重写了

- (BOOL)shouldAutorotate {
    return YES;
}
and
- (NSUInteger)supportedInterfaceOrientations {

    if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]]) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }

}

这个大体上按照预期工作:初始屏幕都是竖屏,即使设备旋转到横屏模式,仍然保持竖屏。视频播放器在最初展示时:

MyVideoPlayerScreen *newVC = [MyVideoPlayerScreen new];
[self.navigationController pushViewController:newVC animated:YES];

当设备是竖屏时,也会在设备转向为横屏时进行旋转 - 到目前为止一切顺利。

问题是,如果我将设备旋转到横屏,则视频播放器也会随之旋转,但是当我通过后退按钮关闭视频播放器屏幕时,底层视图控制器(它应该仅限于竖屏)现在也处于横屏状态。 如果我将设备旋转回竖屏,则视图控制器也会旋转回竖屏,并且从那时起正确地锁定为仅限竖屏。

如何使原始视图控制器(应仅限于竖屏)在弹出其上方的横屏视图控制器时自动返回到竖屏?

这个问题已经被问了无数次,但似乎发布的修复程序都是iOS 8中不再起作用的hack。

更新: 我发现了一个“准确”的解决方法,适用于iOS 8。 在我的 UINavigationController 子类中,我处理了<UINavigationControllerDelegate>协议。 我实现了这个方法:

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    if (![viewController isKindOfClass:[MyVideoPlayerScreen class]]) {

        // if the current orientation is not already portrait, we need this hack in order to set the root back to portrait
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        if (orientation != UIInterfaceOrientationPortrait) {

            // HACK: setting the root view controller to nil and back again "resets" the navigation bar to the correct orientation
            UIWindow *window = [[UIApplication sharedApplication] keyWindow];
            UIViewController *vc = window.rootViewController;
            window.rootViewController = nil;
            window.rootViewController = vc;

        }

    }

}

这至少保留了我想要的UI状态。问题在于,当顶层视图控制器被解除并以动画方式移出屏幕时,底层视图控制器仍处于横向状态;然后突然跳转到纵向状态。虽然不理想,但总比没有好。

更新2: 我应该补充说明我是这样推送第二个视图控制器的:

ViewControllerPortraitLandscape *newVC = [ViewControllerPortraitLandscape new];
[self.navigationController pushViewController:newVC animated:YES];

更新3: 好的,这是完全可行的,适用于iOS 6及以上版本。修复甚至有点合理,尽管它工作的原因似乎没有出现在文档中。基本上,在需要在设备仍处于横向状态时将顶级视图控制器解除时需要重置为纵向的视图控制器中,只需要添加以下内容:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
尽管我的UINavigationController子类完全负责旋转调用,但断点显示在顶层被解除之前,该基础视图控制器上的supportedInterfaceOrientations会被调用,并且在导航控制器上一次性被调用。因此,我推断iOS会自己调用视图控制器本身以确定基础视图控制器应该处于什么方向(而不是询问导航控制器);如果没有显式地重写它,则会返回除了倒置之外的所有参数,因此iOS将其保留在横向模式下。
5个回答

5
原来这是个简单的解决方案:你可以通过UINavigationController的一个子类来驱动整个过程,就像我在这里发布的那样(即不在视图控制器本身实现任何旋转方法),除了任何需要仅支持竖屏的视图控制器,你还需要实现以下内容:
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

使用断点,您可以看到在推送的视图控制器被解除之前,该方法被调用;iOS可能使用此调用来确定“揭示”的视图控制器应处于什么方向(在顶级视图控制器解除后调用导航控制器子类的相同调用)。
在视图控制器中实现此方法后,一切都按预期工作。

1
适用于iOS 7和8。你节省了我很多时间。有许多关于自动旋转问题的答案,而这个解决了我的情况。当切换到横向时,我使用一个新的UINavigationController,但是我的主导航控制器需要拥有这个方法,因为它只支持纵向。谢谢! - aramusss

3

除了展示特定的UIViewController子类外,请使用竖屏模式。这篇文章非常有帮助:~~因为垃圾邮件已被删除~~

将Info.plist设置为支持竖屏、左横屏和右横屏。

实现application:supportedInterfaceOrientationsForWindow:,如下所示:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
// (I specifically want landscape left for the movie viewing)

继承UINavigationController(窗口的rootViewController),并进行如下重写:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait; // enforces the “portrait everything” requirement
}

最后,我必须确保自定义播放器视图控制器能够“覆盖”支持的方向:

- (BOOL)shouldAutorotate
{
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationLandscapeLeft;
}

结果: UIViewController子类呈现视图,但在解除自定义视图控制器时,呈现的视图控制器为Portrait。
来自fantageek文章:"系统将视图控制器支持的方向与应用程序支持的方向(由Info.plist文件或应用程序委托的application:supportedInterfaceOrientationsForWindow:方法确定)相交,以确定是否旋转。"

1
请修复您的链接,因为它导向可疑内容。 - heyfrank

0

我认为你应该改成这样,然后再试一次

-(NSUInteger)supportedInterfaceOrientations {
 if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]]) 
 {
    return UIInterfaceOrientationMaskLandscape;
 } 
 else 
 {
    return UIInterfaceOrientationMaskPortrait;
 }
 }

完全不起作用。第二个屏幕仍然以纵向方式呈现,如果您旋转到横向,则从那时起将被锁定为横向。当您在横向模式下关闭时,底层视图控制器仍保持在横向模式下。 - MusiGenesis

0

这就是如何做。

在呈现和呈现的控制器上实现这3种方法:

- (BOOL)shouldAutorotate {
    return NO; //-- for presented controller use YES
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape; //-- any orientation you need
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

现在你可以在呈现控制器中使用:

[self presentViewController:presentedController animated:true completion:nil];

这样,当您返回到呈现控制器时,它将具有正确的方向。


不行,这也不起作用。如果在这些方法上设置断点,您可以看到原因。当顶层视图控制器被解除时,shouldAutorotate会在顶层视图控制器上调用,它当然返回YES,所以UI保持横向。现在,当您尝试旋转时,shouldAutorotate会在底部视图控制器上调用,它返回NO,因此UI仍然停留在横向。无论您是呈现还是推送顶级vc,都存在相同的问题。 - MusiGenesis

0
我的情况有3个视图控制器:
- 第一个视图控制器:竖屏
- 第二个视图控制器:向右横屏(具有导航控制器,并由第一个视图控制器呈现)
- 第三个视图控制器:竖屏(具有导航控制器,并由第二个视图控制器推出)
我的解决方案已经在这里。希望这可以帮助到您。

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