iOS:禁用子视图的自动旋转

Posted

技术标签:

【中文标题】iOS:禁用子视图的自动旋转【英文标题】:iOS: Disable Autorotation for a Subview 【发布时间】:2011-09-08 20:03:40 【问题描述】:

我有一个支持方向更改的 iPad 应用程序的嵌套视图层次结构。它看起来类似于以下内容。

UIViewController
    UIView
        - UIView
            - UIImageView (disable rotation)
            - UIImageView
            - UIView (disable rotation)
        - UIView
        - UIView
        ...

我想锁定一些子视图的方向,同时允许其他子视图自动旋转和调整大小。我似乎不太明白如何做到这一点。

一种方法似乎是在willAnimateRotationToInterfaceOrientation: 中手动旋转子视图。这并不是特别有吸引力,因为 SDK 正在执行我将要撤消的轮换。

有没有办法简单地禁用子视图的方向更改或其他方法来重组我的层次结构?

【问题讨论】:

一个简单的解决方案:***.com/a/6292506/294884 【参考方案1】:

自动旋转由视图的 UIViewController (shouldAutorotateToInterfaceOrientation:) 处理,因此一种方法是安排层次结构,使可旋转视图由一个视图控制器管理,不可旋转视图由另一个视图控制器管理。这两个 UIViewController 的根视图都需要添加到窗口/超级视图中。

这里的微妙之处在于,如果您在同一级别上有两个视图控制器的视图(即通过addSubview: 添加),则只有第一个视图控制器(通常是窗口的rootViewController)会收到shouldAutorotateToInterfaceOrientation: 消息。

我自己使用这种方法来实现一个可旋转的工具栏,而主视图则没有。

Apple's Technical Q&A QA1688 ("Why won't my UIViewController rotate with the device?") 稍微谈及这个问题。

iOS 6 更新:

自动旋转现在使用UIViewControllershouldAutorotatesupportedInterfaceOrientations 方法。 shouldAutorotate 默认返回 YES,但请记住,除了 rootViewController 之外的视图控制器,其视图是窗口的直接子视图,无论如何都不会收到旋转回调。


iOS 6 示例代码:

使用“单一视图应用程序”模板创建一个新项目,并确保选中“使用情节提要”。我们将使用提供的ViewController 类作为旋转视图控制器(如果愿意,可以重命名它!),并创建第二个UIViewController 子类NonRotatingViewController。虽然这个视图控制器永远不会收到旋转回调,但为了完整和清晰,在NonRotatingViewController.m 中添加以下代码:

- (BOOL)shouldAutorotate

    return NO;

MainStoryboard 文件中,拖出一个新的视图控制器对象并将其类设置为NonRotatingViewController,并将其Storyboard ID 设置为“NonRotatingVC”。当您在那里时,将旋转视图控制器视图的背景颜色更改为清除(非旋转视图将添加到此视图下方),并为每个视图添加一个标签。在AppDelegate.m中,添加如下代码:

#import "NonRotatingViewController.h"

// ...
// ...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    // Override point for customization after application launch.
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    NonRotatingViewController *nonRotatingVC = [mainStoryboard instantiateViewControllerWithIdentifier:@"NonRotatingVC"];
    [self.window addSubview:nonRotatingVC.view];
    return YES;

这只是实例化一个非旋转视图控制器并将其视图直接添加到窗口(注意此时窗口的rootViewController 已经由情节提要设置)。

运行项目。旋转设备并惊叹于一个标签旋转而另一个保持静止的景象!


iOS 6 之前的示例代码:

我在一个新项目中这样做了——一个新的基于视图的应用程序就可以了。添加两个新的视图控制器:RotatingViewControllerNonRotatingViewController。在他们的每个笔尖中,我只是添加了一个标签来描述视图是否应该旋转。添加以下代码:

'RotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

    return YES;

'NonRotatingViewController.m'

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

    if (interfaceOrientation == UIInterfaceOrientationPortrait)     // Or whatever orientation it will be presented in.
        return YES;
    
    return NO;

'AppDelegate.m'

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    RotatingViewController *rotating = [[RotatingViewController alloc] initWithNibName:@"RotatingViewController" bundle:nil];
    self.rotatingViewController = rotating;
    [rotating release];

    NonRotatingViewController *nonRotating = [[NonRotatingViewController alloc] initWithNibName:@"NonRotatingViewController" bundle:nil];
    self.nonRotatingViewController = nonRotating;
    [nonRotating release];

    [self.window addSubview:self.rotatingViewController.view];
    [self.window insertSubview:self.nonRotatingViewController.view belowSubview:self.rotatingViewController.view];

    [self.window makeKeyAndVisible];

    return YES;

我希望这会有所帮助。

【讨论】:

这种方法似乎对我不起作用。我已经配置了一个视图层次结构,其父视图控制器包含两个子视图控制器(portraitViewController 和 rotateViewController),每个子视图控制器都包含一个 UIImageView,其中 shouldAutorotateToInterfaceOrientation 为纵向返回 NO,为旋转返回 YES。如果我在父级中将 shouldAutorotateToInterfaceOrientation 设置为 NO,则方向是静态的,正如预期的那样。但是,在我的父母中将 shouldAutorotateToInterfaceOrientation 设置为 YES 会导致两者都旋转。你有机会发布一些代码吗?我哪里出错了。 @Jason:视图控制器可能非常喜怒无常和特别 - 使用您所描述的自定义“容器”控制器可能是导致问题的原因。我会确保您的子视图控制器视图都直接附加到应用程序窗口。 感谢 StuDev。周围+1。 “容器”控制器肯定是导致问题的原因。 谢谢,这很棘手,这个解决方案对我来说非常有效。我还想补充一点,分离视图意味着我的可旋转视图需要通过触摸事件...这个答案对此有所帮助:***.com/questions/7381155/… 仅供参考,我解决了创建 2 个窗口的问题:一个旋转,第二个 - 固定。每个窗口都有自己的根视图控制器 - 一个固定,另一个 - 旋转:-)【参考方案2】:

这是另一种方式。只需将此代码放入您的 viewController viewDidLoad:

    YourAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        // the view you don't want rotated (there could be a heierarchy of views here):
    UIView *nonRotatingView = [[UIView alloc] initWithFrame:CGRectMake(100,0,30,500)];
    nonRotatingView.backgroundColor = [UIColor purpleColor];

        // make sure self.view and its children are transparent so you can see through to this view that will not be rotated behind self.view.

    [delegate.window insertSubview:nonRotatingView  belowSubview:self.view];

        // you can't put it in the front using:
        //    [delegate.window insertSubview:nonRotatingView aboveSubview:self.view];
        // It will show up the same as before

    // you should declare nonRotatingView as a property so you can easily access it for removal, etc.

【讨论】:

这对我来说非常有效!比其他解决方案更简单。 有人需要解释为什么这会起作用,因为代码并不明显 nonRotatingView 被放置在应用程序的窗口和 ViewControllers 视图之间,并且不受 viewcontroller 管理。 viewcontroller 将只处理其视图的方向更改,并将忽略放置在它和窗口之间的视图。 我已经实现了这一点,因为我的视图控制器中有一个 AVCaptureSession,我希望它不应该旋转,但我希望非旋转视图作为我的视图控制器 self.view 的全宽和全高打开我的相机,但是当我将其设为全尺寸时,视图控制器的 self.view 上的按钮不可见,不知道如何在保持非旋转视图全屏的同时使它们可见【参考方案3】:

我解决这个问题的方法是使用UINotification 来检测自动旋转并反向旋转视图。

在这里找到完整的代码:https://gist.github.com/ffraenz/5945301

- (void)orientationDidChangeNotificationReceived:(NSNotification *)notification

    // find out needed compass rotation
    //  (as a countermeasure to auto rotation)
    float rotation = 0.0;

    if (self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
        rotation = M_PI;
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
        rotation = M_PI / 2.0;
    else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
        rotation = M_PI / (- 2.0);

    // rotate compass without auto rotation animation
    //  ios rotation animation duration is 0.3
    //  this excludes the compassView from the auto rotation
    //  (same effect as in the camera app where controls do rotate and camera viewport don't)
    [UIView animateWithDuration:0.3 animations:^(void) 
        self.compassView.transform = CGAffineTransformMakeRotation(rotation);
    ];

【讨论】:

这适用于 iOS7,但由于某种原因,它不适用于 iOS 8

以上是关于iOS:禁用子视图的自动旋转的主要内容,如果未能解决你的问题,请参考以下文章

iOS 获取子视图的旋转

旋转iOS后子视图控制器高度为零

当项目设置允许所有旋转时禁用特定视图控制器中的旋转

如何在一个视图不应该自动旋转而一个视图应该自动旋转的 iOS 应用程序中处理自动旋转?

在导航控制器中禁用视图控制器的旋转

UIView 子视图不是为 iPad 自动旋转或自动调整大小