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


99

我有一个iPhone应用程序,该应用程序使用UINavigationController来显示向下钻取的界面:首先是一个视图,然后是另一个视图,最多可以看到四个层次。我希望将前三个视图限制为纵向,并且仅应允许最后一个视图旋转为横向。从第四视图返回到第三视图时,我希望所有内容都旋转回纵向。

在iOS 5中,我仅shouldAutorotateToInterfaceOrientation:在每个视图控制器中定义为允许的方向返回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我的第三个视图控制器的方法中,但是它什么也没做。这是错误的调用方法还是错误的调用位置,还是我应该以完全不同的方式实现整个过程?

Answers:


95

我遇到了同样的问题,找到了适合我的解决方案。为了使它的工作,它是不是足以实现- (NSUInteger)supportedInterfaceOrientations你的UINavigationController。您还需要在控制器#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添加任何内容。这对我有用,希望对您有所帮助。


8
您解决了人们几个月来一直试图解决的问题!为此,您值得赞扬!我在UIViewController上的类别中添加了supportedInterfaceOrientations,它对于那些仅纵向肖像的控制器来说是完美的默认设置。
dwery 2012年

3
在SO的100个不同答案中,这是真正有效的答案。谢谢:-)
Christer Nordvik

16
我已经进行了此设置,并且从纵向返回到仅限于横向的视图控制器时仍然遇到错误的方向。它不会自动旋转到地面-其他人仍然看到吗?
Oded Ben Dov

1
如果正在从Segue中显示所需的景观控制器,又没有导航控制器怎么办?
jesses.co.tt

1
将最后一个编辑为NSUInteger作为返回类型。
虔诚的

68

添加一个CustomNavigationController

覆盖其中的这些方法:

-(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;
}

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


5
您真的用这个答案钉了它!非常感谢。你真的帮了很多忙。一段时间以来,这一直是我的大问题。很好。
K2Digital

谢谢!我花了很多时间和精力弄清楚!
bkbeachlabs

您是拯救生命的人。...这也适用于我的应用程序。实际上,我要在我的应用程序中获得的全部内容ViewController都处于“人像”模式,而我的人ViewController处于“景观和人像”模式。我也在iOS 5.0和iOS 6.0中都提供了支持,这就是为什么我很困惑的解决方法,但这是一个很好的解决方案。我在根视图控制器中添加CustomNavigationController并根据需要提供支持。再次感谢。
Bhavin_m 2013年

太棒了 正是我所需要的。谢谢。
mszaro

如果正在从Segue中显示所需的景观控制器,又没有导航控制器怎么办?
jesses.co.tt 2013年

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将旋转控制权交给了根视图控制器,而不是顶视图控件。如果希望单个视图的旋转行为与堆栈中的其他视图不同,则需要在根视图控制器中为其编写特定的大小写。


2
这是一个很好的答案,值得更多的爱。这是其中包含的信息,我在其他任何地方都找不到,这很关键: 如果您希望单个视图的旋转行为与堆栈中的其他视图不同,则需要在根目录中为其写一个特定的格视图控制器
shmim 2014年

我想如果您将NavigationController作为应用程序情节提要的第一个成员作为初始控制器,那么这将起作用,但是当初始控制器只是ViewController时,该怎么办?
鸭子

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];
}

实际上,我确实在根视图控制器中使用了UITabBarController作为实例变量,因为在6.0之前的iOS版本中您不应该将UITabBarController子类化,因此我需要支持iOS 5.0。
布莱恩

@micmdk我有同样的问题。请告知解决方法,我已经复制了您的代码,并将其放入自定义的Navigation Controller和customUabbarController中,并将其他视图控制器设置为Brian的Guided。但仍然无法获得固定的纵向视图控制器。
拉姆S

@布莱恩我有同样的问题。请告知解决方法,我已经复制了您的代码,并将其放入自定义的Navigation Controller和customUabbarController中,并将其他视图控制器设置为Micmdk的Guided。但仍然无法在ios8的肖像中获得固定的视图控制器。
拉姆S

3

我想部分回答我自己的问题。我发现在viewWillAppear我的第三个方法中使用的以下代码行UIViewController

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

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

我很想将其保留在我的应用程序中,因为它可以正常工作。但这感觉不对,所以我想提一个干净的解决方案。


4
如果您将应用保留在原处,则很可能会拒绝它。我也在寻找解决方案,但似乎不可能。我所能找到的就是对这个问题的答案:stackoverflow.com/questions/7196300/…虽然可以,但是由于某种原因,它破坏了我的一个视图控制器中的按钮触摸处理
Lope

我抓住了机会,向苹果公司提交了文件。仍在等待审查...我会及时通知您。
HaraldBögeholz,2012年

苹果公司刚刚批准了我的应用,并实施了此破解,是的!希望以后的iOS更新不会对我产生反作用。同时,我仍然欢迎您提出干净解决方案的建议!
HaraldBögeholz,2012年

1
@HaraldBögeholzGrt建议。但是当我使用ARC在新的xCode中工作时,我无法使用ans。我收到“ ARC PerformSelector不允许将NSInteger(aka int)转换为id的消息,因为其选择器未知,可能会导致泄漏”我该如何解决呢?如果我们有解决方案,我真的很想您。当我关闭ARC进行Perticular View时,由于某些泄漏数据,导致应用程序崩溃。请建议我
希塔斯,

4
使用私有API永远不是解决方案。
哈维尔·索托

2

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

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}

1

由于这是一个备受关注的线程。我以为我会补充我认为最简单的答案。这适用于ios8及更高版本

-(BOOL)shouldAutorotate
{
    return YES;
}

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

而已。请享用!

哦,我的ViewControllers嵌入在导航控制器中,无需任何子类化或配置。


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设置为通过以前的VCLandscapeOK值存储的内容。

就像我说的那样,这可能并不适合所有人,但对我来说却很好,而且我不必担心将代码添加到每个视图控制器中,因此能够将所有这些都集中在UINavigationController子类中。

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



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;
    }
}

2
问题在于,除非该视图是根视图(该问题不在此视图中),否则不应调用shouldAutorotate。
Brian

不管出于何种原因,返回类型都NSUInteger将发出警告,尽管它是正确的var类型。如果您使用OCD UIInterfaceOrientationMask代替。
克里斯J

而且,该解决方案非常适合我有的模态视图控制器(相机视图),而无需子类化UINavigationController。我猜模态是独立对待的。与原始问题无关,但是对于明智的设计有用。
克里斯J

0

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

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

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }

Trunal,这已经有几个月的历史了,但是您能否提供更多有关如何检测位于顶部的视图控制器的详细信息。具体来说,我不确定你要去这里做什么[[Utils getAppDelegate] appNavigationController]。我猜测的实用程序是您上的一门课,但是您在其中所做的事我无所适从。任何帮助将是巨大的!
2014年

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];
}

0

您可以在所有View Controller类中实现和覆盖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]
    }
}

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。在我的情况,我想展示LoginViewControllerPortrait模式以外ViewControllerslandscape模式。


-1

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

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

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


3
正如我写的那样,我做到了。这使我的视图控制器旋转,但它并不会迫使它回到唯一支持的方向,当我返回到它。
HaraldBögeholz2012年

这对我不起作用。即使将其包含在我的视图控制器中,视图仍会旋转。是什么赋予了?
shim 2012年

为什么不赞成maxfiresolutions?它对我有用,@ Flo给出的相同方法也有很多缺点。
ViruMax 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.