【问题标题】:Prevent some view controller in a navigation controller from rotating防止导航控制器中的某些视图控制器旋转
【发布时间】:2024-01-04 18:18:01
【问题描述】:

我有一个导航控制器,它同时显示两个视图控制器。它们是分层的,最前面的控制器可以向下拖动以显示下面的控制器。 我的应用委托已配置,因此该应用仅在显示背面视图时才允许界面方向,如下所示。

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if (self.revealNavigationController.isViewRevealed)
    {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }

    return UIInterfaceOrientationMaskPortrait;
}

当设备旋转时,这自然会导致前后视图控制器旋转,但我只对旋转后视图控制器感兴趣。 有没有办法在最前面的视图控制器中完全禁用旋转?

【问题讨论】:

  • 您的问题是用户可以显示后视图,侧向旋转,然后隐藏它并且主视图仍然是侧向的,还是主视图即使在屏幕外也会旋转?
  • @axiixc 问题是主视图在离屏时旋转。视图控制器并不真正意味着支持方向更改,因此如果您显示后视图,旋转到横向,旋转回纵向并再次隐藏后视图以便呈现主视图,主视图就会混乱。理想情况下,我希望主视图不可能旋转。这将解决您提到的两个问题。因此,如果用户显示后视图、侧向旋转并隐藏它,主视图仍然是纵向的。

标签: iphone objective-c uinavigationcontroller rotation uiinterfaceorientation


【解决方案1】:

您需要使用 iOS5 中添加的视图控制器包含 API。基本上,您需要做的是在您不再希望它参与旋转事件时移除您的一个视图控制器,并在它再次准备好时将其添加回来。一个样本将是......

@implementation AXRotationDemoViewController

- (id)init
{
    if ((self = [super init]))
    {
        self.oneViewController = [[AXLoggingViewController alloc] init];
        self.oneViewController.interfaceOrientations = UIInterfaceOrientationMaskPortrait;

        self.twoViewController = [[AXLoggingViewController alloc] init];
        self.twoViewController.interfaceOrientations = UIInterfaceOrientationMaskAllButUpsideDown;
    }

    return self;
}

- (void)viewDidLoad
{
    [self.oneViewController willMoveToParentViewController:self];
    [self addChildViewController:self.oneViewController];
    [self.view addSubview:self.oneViewController.view];
    [self.oneViewController didMoveToParentViewController:self];

    [self.twoViewController willMoveToParentViewController:self];
    [self addChildViewController:self.twoViewController];
    [self.view addSubview:self.twoViewController.view];
    [self.twoViewController didMoveToParentViewController:self];

    self.oneViewController.view.backgroundColor = [UIColor redColor];
    self.twoViewController.view.backgroundColor = [UIColor greenColor];

    UIButton * showBackButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [showBackButton setTitle:@"Toggle Back/Front" forState:UIControlStateNormal];
    [showBackButton addTarget:self action:@selector(_toggle:) forControlEvents:UIControlEventTouchUpInside];
    [showBackButton sizeToFit];
    showBackButton.center = self.view.center;
    [self.view addSubview:showBackButton];
}

- (void)_toggle:(id)sender
{
    if ([self.childViewControllers containsObject:self.oneViewController])
    {
        [self.oneViewController.view removeFromSuperview];
        [self.oneViewController removeFromParentViewController];
    }
    else
    {
        [self.oneViewController willMoveToParentViewController:self];
        [self addChildViewController:self.oneViewController];
        [self.view addSubview:self.oneViewController.view];
        [self.oneViewController didMoveToParentViewController:self];

        UIWindow * window = self.view.window;
        UIViewController * hack = window.rootViewController;
        window.rootViewController = nil;
        window.rootViewController = hack;
    }
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    CGRect halfScreenFrame = self.view.bounds;
    halfScreenFrame.size.height /= 2;

    self.oneViewController.view.frame = halfScreenFrame;
    self.twoViewController.view.frame = CGRectOffset(halfScreenFrame, 0, halfScreenFrame.size.height);
}

- (NSUInteger)supportedInterfaceOrientations
{
    if ([self.childViewControllers containsObject:self.oneViewController])
        return self.oneViewController.supportedInterfaceOrientations;

    return self.twoViewController.supportedInterfaceOrientations;
}

@end

但是您可能会注意到,没有官方方法可以真正告诉 iOS 您已经更改了 supportedInterfaceOrientations 的值,因此您最好的选择是 a) 使用上面示例中的 hack 来强制 iOS 重做所有操作或 b) 在设备再次处于纵向状态之前,不要让用户关闭后视图。

这里是我整理的sample,如果这不能很好地解释事情的话。

【讨论】:

  • 这将是理想的方法,但在这种情况下我没能成功。对于我的解决方案,请参阅*.com/a/16354167/486845
【解决方案2】:

是的,看看 UIViewController 中的-supportedInterfaceOrientations 方法。您返回一个 UIInterfaceOrientationMask 位掩码,指定您希望 UIViewController 子类支持的方向。

【讨论】:

  • 我刚刚尝试在我最前面的一个控制器中返回-shouldAutorotate 中的NO-supportedInterfaceOrientations 中的UIInterfaceOrientationMaskPortrait。虽然 -supportedInterfaceOrientations 在视图呈现但不旋转时被调用,但它似乎被忽略了。视图控制器仍然尝试旋转视图并满足我在横向方向的自动布局约束。
  • -shouldAutorotate 没有被调用。
【解决方案3】:

axiixc 建议的解决方案通常更好,因此根据您的问题,您应该look at his answer 但是,这并没有解决我的问题。我有一个UINavigationController,在其中我将视图控制器的视图作为子视图插入到索引 0 处。这样,视图将位于后面。然后我将UINavigationBar 设置为可拖动的,这样当它向下拖动时,它会显示后面的视图。然后最前面的视图将随导航栏一起移动。 正如 axiixc 所建议的那样,我没有设法让它与引入 iOS 5 的视图控制器包含 API 一起工作。

相反,我从导航控制器中删除了视图,并在应用程序启动时将其直接添加到UIWindow,我自己处理了该视图的旋转并禁用了所有其他视图的旋转(也就是说,我禁用了它在导航控制器上)。

这就是我在-application:didFinishLaunchingWithOptions:中添加视图的方式

self.revealViewController = [[RevealViewController alloc] init];
[self.window insertSubview:self.revealViewController.view atIndex:0];

然后,在那之后我注册了UIDeviceOrientationDidChangeNotification

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];

我的didRotate: 看起来像这样:(这是受 * 上另一个答案的启发,我现在无法再次找到,抱歉)

- (void)didRotate:(NSNotification *)notification
{
    // Only rotate if the view is revealed
    if (self.revealNavigationController.isViewRevealed)
    {
        UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
        [self updateForDeviceOrientation:orientation animated:YES];
    }
}

我打电话给-updateForDeviceOrientation:animated:

- (void)updateForDeviceOrientation:(UIDeviceOrientation)orientation animated:(BOOL)animated
{
    CGFloat degrees = 0.0f;
    switch (orientation)
    {
        case UIDeviceOrientationLandscapeLeft:
            degrees = 90.0f;
            break;
        case UIDeviceOrientationLandscapeRight:
            degrees = -90.0f;
            break;
        case UIDeviceOrientationPortrait:
            degrees = 0.0f;
            break;
        default:
            break;
    }

    CGFloat duration = (animated) ? [UIApplication sharedApplication].statusBarOrientationAnimationDuration : 0.0f;

    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:duration animations:^{
        // Rotate view
        weakSelf.revealViewController.view.transform = CGAffineTransformIdentity;
        weakSelf.revealViewController.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));

        // Resize view for rotation
        CGFloat width, height;
        if (UIDeviceOrientationIsLandscape(orientation))
        {
            width = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
            height = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
        }
        else
        {
            width = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
            height = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
        }

        CGSize newSize = CGSizeMake(width, height);

        CGRect viewBounds = weakSelf.revealViewController.view.bounds;
        viewBounds.size = newSize;
        weakSelf.revealViewController.view.bounds = viewBounds;

        CGRect viewFrame = weakSelf.revealViewController.view.frame;
        viewFrame.size = newSize;
        weakSelf.revealViewController.view.bounds = viewFrame;
    }];
}

【讨论】: