【问题标题】:UINavigationController and autorotationUINavigationController 和自动旋转
【发布时间】:2010-11-01 12:17:46
【问题描述】:

我有一个 UIViewController,它在 shouldAutorotateToInterfaceOrientation: 中返回 YESUIDeviceOrientationPortraitNO 为其他所有内容。使用堆栈顶部的视图,我使用pushViewController:animated: 推送一个新的UIViewController。对于shouldAutorotateToInterfaceOrientation: 中的任何内容,新控制器都会返回YES

第一个视图拒绝旋转(如预期的那样)。推送第二个视图后,用户可以旋转设备,并且 UI 将旋转(也如预期的那样)。如果第二个视图处于横向模式并且用户按下返回按钮(调用popViewControllerAnimated:),第一个视图将显示为旋转(unexpected!)。

如果用户将设备旋转回纵向,则视图将旋转,然后像以前一样停留在纵向模式。这可行,但在用户旋转回来之前,这对用户来说很难看。所以我正在寻找一种方法让这个视图保持纵向模式。

到目前为止,我发现的唯一解决方法是使用-[UIDevice setOrientation:],它会引发警告(orientation 是只读的),但因为它是实际定义的,所以可以工作。这是一个巨大的黑客,我想要一个真正的解决方案。为了寻找真正的解决方案,我将 GDB 附加到照片应用程序 (MobileSlideshow.app) 并发现它也使用了-[UIDevice setOrientation:]。作为一个内部应用程序,虽然我猜他们有不同的规则。

是否有正确的方法来实现预期的自转行为?

【问题讨论】:

  • 我相信在应用商店中有(大概)使用 setOrientation 的应用示例。一个是 Tweetie,它可以在写推文时强制使用横向键盘。也许他们侥幸逃脱,因为这不是默认设置。
  • 现在是 2014 年,这个问题仍然出现在 ios 8 上。有没有人找到这个问题的真正答案,看起来不像是黑客?

标签: iphone cocoa-touch uinavigationcontroller rotation


【解决方案1】:

UIDevice setOrientation 的合法替代方法如下:

UIWindow* window = UIApplication.sharedApplication.keyWindow;
UIView* view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

这会强制当前视图控制器评估其方向,调用 shouldAutorotateToInterfaceOrientation 并从任何禁止的方向切换。

例如以下代码将用于支持所有方向但其父级仅支持横向的视图控制器中:

- (void)popBack
{
    [self.navigationController popToRootViewControllerAnimated:YES]; 
}

- (IBAction)backPressed:(id)sender
{   
    portraitOrientationPermitted = NO;

    // Force the framework to re-evaluate the interface orientation.
    UIWindow* window = UIApplication.sharedApplication.keyWindow;
    UIView* view = [window.subviews objectAtIndex:0];
    [view removeFromSuperview];
    [window addSubview:view];

    [self performSelector:@selector(popBack) withObject:nil afterDelay:0.8];

    portraitOrientationPermitted = YES;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return portraitOrientationPermitted || 
        UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

【讨论】:

  • 在 iOS 7 上对我不起作用。但是,删除窗口的 rootViewController 并将其放回原处。只是把它放在那里,以防有人遇到同样的问题。
【解决方案2】:

这是一个旧帖子,但尚未解决。我想分享我的解决方案,以供其他可能头疼的人使用。

目标: UINavigationController 及其堆栈中的大多数视图控制器都固定为纵向,除了堆栈中的一个视图控制器被允许同时旋转为纵向和横向。

问题: 直观地,我通过检查 topViewController 是否是 rotableViewController 来设置选择性的 shouldAutorotateToInterfaceOrientation。然而,在横向模式下从 rotableViewController 弹出后,导航控制器现在以横向模式显示,尽管这是不允许的。

解决方案:杀手锏是在viewWillAppear 处禁止旋转,并在没有动画的情况下呈现和关闭modalViewController。

  1. 一个 appViewController 作为宿主添加到窗口中 viewController,即比RootViewController更root;
  2. 在 appViewController 中添加了一个 navigationController,其中 委托设置为 appViewController;
  3. 在 AppViewController 中


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES;
    return canRotate;
}


- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewDidAppear:animated];
    canRotate = ([navigationController.topViewController isKindOfClass:[MyRotatable class]]);
}


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewWillAppear:animated];
    if (![navigationController.topViewController isKindOfClass:[MyRotatable class]]) {
        canRotate = NO;
        UIViewController * blanck = [[UIViewController alloc] initWithNibName:nil bundle:nil];
        [self presentModalViewController:blanck animated:NO];
        [self dismissModalViewControllerAnimated:NO];
        [blanck release];
    }
}

【讨论】:

    【解决方案3】:

    iOS 5 增加了+[UIViewController attemptRotationToDeviceOrientation],为我解决了这个问题。

    【讨论】:

    • 根据文档(“尝试将所有窗口旋转到设备的方向。”)似乎这是针对反向用例,即您推送一个支持界面的新视图控制器上一个不支持的方向。
    【解决方案4】:

    我正要告诉你,可能没有办法,但后来我有了一个想法。很难做到正确,但是如果您使用两个单独的UINavigationControllers,您可能能够使其工作:一个控制根视图并禁止旋转,另一个用于允许它的子视图。您将手动处理与根控制器和子控制器之间的转换。

    您必须修补子导航控制器才能拥有正确的后退按钮。而且,当然,您必须自己处理后退按钮。您可能必须使用一个虚拟的UINavigationBar 来制作从一个导航控制器到下一个导航控制器的动画,以便过渡看起来正确。您还必须为导航控制器之间的“推送”过渡设置动画,这可能需要进行一些调整才能使其看起来正确。你必须:

    1. 配置一个虚拟导航栏以与传出导航控制器完全匹配,并将其直接放在导航控制器栏的顶部。 (您可以复制当前视图控制器的UINavigationItem 的配置并推送它)
    2. 将新的导航控制器放置在屏幕外的右边缘
    3. 为新旧控制器的帧从右向左移动动画
    4. 为传入的视图控制器创建UINavigationItem 的副本并将其推送到虚拟导航栏上
    5. 动画完成后,从视图中移除虚拟UINavigationBar,以及传出导航控制器。

    所有这些工作量很大,但如果你非常聪明(而且非常顽强),你也许可以让它发挥作用。我很想看看结果!

    也就是说,您最好还是使用setOrientation: 并在 App Store 批准流程中把握机会;-)

    【讨论】:

      【解决方案5】:

      使用 3.0 操作系统再试一次(现在我们可以谈论它了)。在 3.0 下已经解决了两种特殊情况:

      1. 在横向视图上显示纵向模式视图,反之亦然。一个示例是 Mail 中用于查看附件的 3.0 之前的行为。当您关闭附件视图时,您可以将 PDF 旋转到横向并返回邮件的纵向视图。 (现在我们在 3.0 中有横向消息视图,这种行为似乎已经消失了)。

      2. 在视图堆栈中混合方向并在弹出堆栈时恢复正确的方向。一个例子是电影的表格视图和 YouTube 应用中的电影视图之间的转换。

      似乎存在一些棘手的美学问题,即所有排列的默认过渡应该如何看待。如果你在慢动作中查看,会有一些奇怪的元素,但它胜过在你自己的代码中向后弯腰。

      【讨论】:

      • 我试过了,#1 似乎工作正常,但#2 不适合我。我用表格视图控制器建立了一个导航控制器项目。根视图不能旋转,但推送视图可以。当您弹出第二个视图时,它将保持旋转。事实上,nav-bar-items 看起来很混乱,即使在弹出第二个视图之后也会显示它的标题。
      【解决方案6】:

      我为这个问题找到了一个很好的解决方法。线索是支持UINavigationControllerall 视图的all 方向。

      我在控制器中有 2 个视图。根视图是只支持LandscapeRight,二是同时支持LandscapeRightPortrait

      第二个视图shouldAutorotateToInterfaceOrientation 方法看起来和往常一样:

      - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
      {
          return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
                 (interfaceOrientation == UIInterfaceOrientationPortrait);
      }
      

      解决方法本身包含在根视图源中 现在根视图根据代码旋转,但用户看不到它。

      //auxiliary function
      -(void) fixOrientation:(UIInterfaceOrientation)orientation
      {
          if (orientation == UIInterfaceOrientationPortrait)
              self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
          else if (orientation == UIInterfaceOrientationLandscapeRight)
              self.view.transform = CGAffineTransformMakeRotation(0);
      }
      
      -(void) viewWillAppear:(BOOL)animated
      {
          [self fixOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
      }
      
      - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
      {
          [self fixOrientation:interfaceOrientation];
          //notice, that both orientations are accepted
          return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
                 (interfaceOrientation == UIInterfaceOrientationPortrait);
      }
      
      //these two functions helps to avoid blinking
      - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
      {
          [UIView setAnimationsEnabled:NO]; // disable animations temporarily
      
      }
      
      - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
      {
          [UIView setAnimationsEnabled:YES]; // rotation finished, re-enable them
      }
      

      【讨论】:

        【解决方案7】:

        因此,从 iOS 1 到 iOS 4,这个恼人的 bug 出现了很长的路要走。

        我相信最好的解决方案是复制 this bug 并让 Apple 知道我们真的希望修复它。我刚刚在错误 ID 8478525 下再次报告了它。

        【讨论】:

          猜你喜欢
          • 2010-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-03
          • 2010-11-14
          • 2011-09-29
          • 2012-04-07
          • 2010-10-21
          相关资源
          最近更新 更多