19

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

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

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

一种方法似乎是在willAnimateRotationToInterfaceOrientation:. 鉴于 SDK 正在执行我将要撤消的轮换,这并不是特别有吸引力。

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

4

3 回答 3

22

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

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

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

Apple 的技术问答 QA1688(“为什么我的 UIViewController 不会随设备旋转?”)稍微谈到了这个问题。


iOS 6 更新:

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


iOS 6 的示例代码:

使用“Single View Application”模板创建一个新项目,并确保选中“Use Storyboards”。我们将使用提供的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;
}

我希望这有帮助。

于 2011-09-08T20:21:02.397 回答
10

这是另一种方式。只需将此代码放入您的 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.
于 2013-01-18T22:16:10.517 回答
4

我解决这个问题的方法是使用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);
    }];
}
于 2013-07-07T23:17:46.303 回答