iOS 6:如何将某些视图限制为纵向并允许其他人旋转?

时间:2012-09-27 21:34:45

标签: ios iphone ios6 autorotate

我有一个使用UINavigationController来展示向下钻取界面的iPhone应用程序:第一个视图,然后是另一个视图,最多四个级别。我希望前三个视图仅限于纵向,只允许最后一个视图旋转到横向。当从第四个视图返回到第三个视图并且第四个视图处于横向时,我希望所有内容都旋转回肖像。

在iOS 5中,我只是在每个视图控制器中定义shouldAutorotateToInterfaceOrientation:,以便为允许的方向返回YES。所有工作都如上所述,包括返回肖像,即使从视图控制器#4返回到#3时设备被横向保持。

在iOS 6中,所有视图控制器都会旋转到横向,打破那些不适合的视图。 iOS 6发行说明说

  

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

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

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

告诉我的MainNavigationController期望的行为。

以下是问题:如果我现在以纵向模式导航到第四个视图并将手机翻过来,则旋转到横向。现在我按下后退按钮返回我的第三个视图,该视图应该仅适用于肖像。但它不会回转。我该怎么做呢?

我试过

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

在我的第三个视图控制器的viewWillAppear方法中,但它没有做任何事情。这是错误的调用方法,也可能是错误的调用方法,或者我应该以完全不同的方式实现整个事情?

15 个答案:

答案 0 :(得分:95)

我遇到了同样的问题,并找到了适合我的解决方案。 为了使它工作,足以在你的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添加任何内容。 这对我有用,我希望它会对你有帮助。

答案 1 :(得分:67)

添加 CustomNavigationController

在其中覆盖这些方法:

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

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

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

现在在plist中添加所有方向

enter image description here

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

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

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

答案 2 :(得分:9)

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

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

enter image description here

接下来,制作一个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控制旋转到根视图控制器而不是顶视图控制器。如果您希望单个视图的旋转行为与堆栈中的其他视图的行为不同,则需要在根视图控制器中为其编写特定的大小写。

答案 3 :(得分: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];
}

答案 4 :(得分:3)

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

UIViewController

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

我很想把它留在我的应用程序中,因为它只是起作用。但它感觉不对,所以我想提出一个清晰的解决方案。

答案 5 :(得分:1)

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

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}

答案 6 :(得分:1)

因为这是一个受到高度关注的线程。我想我会加上我认为最简单的答案。这适用于ios8及以上

-(BOOL)shouldAutorotate
{
    return YES;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

就是这样。享受!

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

答案 7 :(得分: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;
    }
}

答案 8 :(得分:0)

这可能对每个人都不起作用,但对我来说效果很好。而不是实施......

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

在我所有控制器的viewWillAppear中,我决定通过覆盖UINavigationControllerDelegate方法navigationController:willShowViewController:animated:

将此过程集中在我的UINavigationController子类中
- (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子类中

希望这可以帮助某人,因为它帮助了我。 谢谢,杰里米。

答案 9 :(得分:0)

转到Info.plist文件并进行更改enter image description here

答案 10 :(得分: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;
    }
    

答案 11 :(得分:0)

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

使用 UITabbarController 时,尝试更改 self.viewControllers.lastObject @ micmdk的 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];
}

答案 12 :(得分: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模式显示ViewControllers landscape {/ 1}}。

答案 13 :(得分:0)

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

但是,在一个非常重要的用户界面中,您可能会遇到一个问题,无法将其添加到几十个类中,并且您不希望将它们全部作为支持它的几个常见子类(MyBaseTableViewController,MyBaseNavigationController和MyBaseTabBarController)。

由于您无法直接在UIViewController上覆盖这些方法/ var,您可以在其子类上执行此操作,这些子类通常是您的基类,如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]
    }
}

答案 14 :(得分:-1)

请使用以下方法解决此问题

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

只返回你想要的方向!!!