100

我有一个 iPhone 应用程序,它使用 aUINavigationController来呈现向下钻取界面:首先是一个视图,然后是另一个视图,最多四层深度。我希望将前三个视图限制为纵向,并且只允许最后一个视图旋转到横向。当从第四个视图返回到第三个视图并且第四个视图处于横向时,我希望所有内容都旋转回纵向。

在 iOS 5shouldAutorotateToInterfaceOrientation:中,我只是在每个视图控制器中定义为允许的方向返回 YES。一切都如上所述,包括从视图控制器 #4 返回到 #3 时,即使设备处于横向状态,也可以返回纵向。

在 iOS 6 中,所有视图控制器都旋转为横向,破坏了那些不应该这样做的。iOS 6 发行说明说

更多的责任正在转移到应用程序和应用程序委托身上。现在,iOS 容器(例如UINavigationController)不会咨询它们的子容器来确定它们是否应该自动旋转。[...] 每当设备旋转或视图控制器以全屏模式呈现样式呈现时,系统都会向最顶层的全屏视图控制器(通常是根视图控制器)询问其支持的界面方向。此外,仅当此视图控制器从其shouldAutorotate方法返回 YES 时才会检索支持的方向。supportedInterfaceOrientationsForWindow:[...] 系统通过将应用程序的方法返回的值与supportedInterfaceOrientations最顶层全屏控制器的方法返回的值相交来确定是否支持方向。

所以我进行了子类化UINavigationController,给了我MainNavigationController一个布尔属性landscapeOK并用它来返回允许的方向supportedInterfaceOrientations。然后在我的每个视图控制器的viewWillAppear:方法中,我都有这样的一行

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

告诉我MainNavigationController想要的行为。

问题来了:如果我现在以纵向模式导航到我的第四个视图并将手机翻过来,它会旋转到横向。现在我按下后退按钮返回到我的第三个视图,该视图应该只用于纵向。但它不会旋转回来。我如何让它做到这一点?

我试过了

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

viewWillAppear我的第三个视图控制器的方法中,但它什么也没做。这是错误的调用方法还是错误的调用位置,或者我应该以完全不同的方式实现整个事情?

4

15 回答 15

96

我遇到了同样的问题,并找到了适合我的解决方案。为了使它工作,在你的 UINavigationController中实现是不够的。- (NSUInteger)supportedInterfaceOrientations您还需要在控制器#3 中实现此方法,这是弹出控制器#4 后第一个仅显示纵向的方法。所以,我的 UINavigationController 中有以下代码:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

在视图控制器 #3 中,添加以下内容:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

您不需要向视图控制器#1、#2 和#4 添加任何内容。这对我有用,我希望它会帮助你。

于 2012-10-26T16:25:18.563 回答
69

添加一个自定义导航控制器

覆盖其中的这些方法:

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

现在在 plist 中添加所有方向

在此处输入图像描述

在视图控制器中只添加所需的:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

这些方法覆盖导航控制器方法

于 2012-11-02T11:52:45.647 回答
9

在查看了关于 SO 的无数类似问题中的每个答案之后,没有一个答案对我有用,但它们确实给了我一些想法。这是我最终解决问题的方法:

首先,确保项目目标中支持的界面方向包含旋转视图所需的所有方向。

在此处输入图像描述

接下来,创建一个类别UINavigationController(因为 Apple 说不要对其进行子类化):

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

将该类别和您希望能够旋转的视图控制器(我将称之为RotatingViewController)导入您的最高级别视图控制器,该控制器应包含您的导航控制器。在该视图控制器中,实现shouldAutorotate如下。请注意,这不应与您要旋转的视图控制器相同。

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

最后,在您的,中RotatingViewController实现如下:shouldAutorotatesupportedInterfaceOrientations

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

您需要这样做的原因是因为 iOS 6 将旋转控制权交给了根视图控制器,而不是顶视图控制器。如果您希望单个视图的旋转行为与堆栈中的其他视图不同,则需要在根视图控制器中为其编写特定案例。

于 2013-04-22T16:53:06.183 回答
4

我没有足够的声誉来评论@Brian 的答案,所以我会在这里添加我的注释。

Brian 提到 iOS6 为 rootViewController 提供了旋转控制 - 这不仅可以是上面提到的 UINavigationController,还可以是 UITabBarController,它是为我设计的。我的结构如下所示:

  • UITabBarController
    • UINavigationController
      • UIViewControllers ...
    • UINavigationController
      • UIViewControllers ...

所以我首先在自定义 UITabBarController 中添加方法,然后在自定义 UINavigationController 中,最后在特定 UIViewController 中添加方法。

来自 UITabBarController 和 UINavigationController 的示例:

- (BOOL)shouldAutorotate {
    return [self.viewControllers.lastObject shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.viewControllers.lastObject supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];
}
于 2013-07-25T15:48:49.487 回答
3

我想对我自己的问题给出部分答案。我发现在viewWillAppear我的第三个方法中使用的以下代码行UIViewController可以工作:

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

但我真的不喜欢这个解决方案。它使用一种技巧来分配只读属性,根据 Apple 文档,该属性表示设备的物理方向。这就像告诉 iPhone 跳转到用户手中的正确方向。

我很想把它留在我的应用程序中,因为它很简单。但这感觉不对,所以我想把这个问题留给一个干净的解决方案。

于 2012-09-29T11:13:02.857 回答
1

这是我在 ios 6.0 中用于方向支持的

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}
于 2013-04-22T13:04:35.900 回答
1

因为这是一个高度关注的线程。我想我会添加我认为最简单的答案。这适用于 ios8 及更高版本

-(BOOL)shouldAutorotate
{
    return YES;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

而已。享受!

哦,我的 ViewController 嵌入在导航控制器中,我不需要以任何方式对其进行子类化或配置。

于 2016-01-20T07:44:49.163 回答
0

这可能不适用于所有人,但对我来说非常有用。而不是实施...

[(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

在我所有控制器的 viewWillAppear 中,我决定通过覆盖UINavigationControllerDelegate方法将这个过程集中在我的 UINavigationController 子类中navigationController:willShowViewController:animated:

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

    self.previousVCLandscapeOK = self.isLandscapeOK; // Store the current VC's orientation preference before pushing on the new VC so we can set this again from within the custom "back" method
    self.isLandscapeOK = NO; // Set NO as default for all VC's
    if ([viewController isKindOfClass:[YourViewController class]]) {
        self.isLandscapeOK = YES;
    }
}

我发现从导航堆栈中弹出 VC 时不会调用此委托方法。这对我来说不是问题,因为我正在处理 UINavigationController 子类中的后退功能,这样我就可以为特定的 VC 设置正确的导航栏按钮和操作,就像这样......

if ([viewController isKindOfClass:[ShareViewController class]]) {

    UIButton* backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 57, 30)];
    [backButton setImage:[UIImage imageNamed:@"back-arrow"] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem* backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backButtonItem;

    UIImageView* shareTitle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"share-title"]];
    [shareTitle setContentMode:UIViewContentModeScaleAspectFit];
    [shareTitle setFrame:CGRectMake(0, 0, shareTitle.frame.size.width - 10, shareTitle.frame.size.height - 10)];
    viewController.navigationItem.titleView = shareTitle;

} else if(...) {
    ...
}

这是我back处理将 VC 从堆栈中弹出并设置适当的旋转首选项的方法...

- (void)back {
    self.isLandscapeOK = self.previousVCLandscapeOK;
    self.previousVCLandscapeOK = NO;
    [self popViewControllerAnimated:YES];
}

所以,正如你所看到的,基本上所有正在发生的事情是我首先为我设置两个属性......

@property (nonatomic) BOOL isLandscapeOK;
@property (nonatomic) BOOL previousVCLandscapeOK;

其中navigationController:willShowViewController:animated:将确定即将呈现的 VC 中支持的方向。弹出 VC 时,我的自定义“返回”方法被调用,然后我将 isLandscapeOK 设置为通过 previousVCLandscapeOK 值存储的内容。

正如我所说,这可能不适用于每个人,但它对我来说非常有用,而且我不必担心向我的每个视图控制器添加代码,我能够将它全部集中在 UINavigationController 子类中。

希望这对我有帮助。谢谢,杰里米。

于 2013-05-01T02:46:28.817 回答
0

转到您的 Info.plist 文件并进行更改在此处输入图像描述

于 2013-08-02T22:08:45.630 回答
0

我想让我的所有 VC 都锁定为纵向,除了一个。这对我有用。

  1. 在 plist 文件中添加对所有方向的支持。
  2. 在根视图控制器中,检测窗口顶部的视图控制器类型,并在supportedInterfaceOrientations方法中相应地设置应用程序的方向。例如,我需要我的应用程序仅在 webview 位于堆栈顶部时旋转。这是我在 rootVC 中添加的内容:

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }
    
于 2014-01-24T02:20:13.010 回答
0

您只想强制 iOS 6 应用程序肖像,然后您可以添加到以下方法的 UIViewController 子类

- (BOOL)shouldAutorotate {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return NO;
    }
}


- (NSUInteger)supportedInterfaceOrientations {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return UIInterfaceOrientationMaskAll;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}
于 2013-04-23T10:45:56.223 回答
0

我没有足够的声誉来回答@micmdk 回复下的@Ram S 问题,所以我将在此处添加我的注释。

当您使用UITabbarController时,尝试将@micmdk代码中的 self.viewControllers.lastObject 更改为self.selectedViewController ,如下所示:

- (BOOL)shouldAutorotate {
    return [self.selectedViewController shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.selectedViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
于 2015-07-10T16:17:52.873 回答
0

我已经解决了同样的问题。

如果您使用UINavigationController来推送视图控制器,则必须设置以下方法。

extension UINavigationController{

    override open var shouldAutorotate: Bool {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return true
        }
        return false
    }

    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight

    }
    override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight
    }
}

代替你想显示的LoginViewController使用。UIViewController就我而言,我想LoginViewController在模式中显示Portrait其他模式。ViewControllerslandscape

于 2017-08-26T05:57:31.917 回答
0

您可以在您的所有视图控制器类中实现和覆盖 shouldAutorotate() 和 supportedInterfaceOrientations var,这些类应该以与应用程序的 PLIST 中定义的方向不同的方向呈现。

但是,在一个重要的用户界面中,您可能会遇到将其添加到数十个类中的问题,并且您不想将所有这些类都设为支持它的几个常见的子类(MyBaseTableViewController、MyBaseNavigationController 和 MyBaseTabBarController)。

由于您不能直接覆盖 UIViewController 上的这些方法/变量,因此您可以在其子类上执行此操作,这些子类通常是您的基类,如 UITableViewController、UINavigationController 和 UITabBarController。

因此,您可以实现一些扩展并仍然设置 MyPreciousViewController 以与所有其他人(如 Swift 4 代码片段)不同的方向显示:

extension UITableViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    if let last = self.navigationController?.childViewControllers.last,
        last != self {
            return last.supportedInterfaceOrientations
    } else {
        return [.portrait]
    }
    }
}

extension MyPreciousViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    return [.portrait,.landscape]
    }
}


extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}


extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}
于 2018-03-07T13:31:19.630 回答
-1

Please use the following method to solve this issue

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

return only the orientation you want!!!

于 2012-10-10T19:14:57.003 回答