24

我有一个 UIViewController 可以为其他所有内容返回YES。使用堆栈顶部的那个视图,我用来推送一个新的. 新控制器返回.shouldAutorotateToInterfaceOrientation:UIDeviceOrientationPortraitNOpushViewController:animated:UIViewControllerYESshouldAutorotateToInterfaceOrientation:

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

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

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

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

4

7 回答 7

6

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);
}
于 2011-11-14T20:15:26.160 回答
5

这是一个旧帖子,但因为它还没有解决。我想分享我的解决方案,以供其他可能头疼的人使用。

Objective: UINavigationController 和它的堆栈中的大多数视图控制器固定在纵向,除了堆栈中的一个视图控制器被允许旋转到纵向和横向。

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

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

  1. 一个appViewController作为宿主viewController添加到窗口中,即比RootViewController更root;
  2. 一个navigationController被添加到appViewController,委托设置为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];
    }
}
于 2011-03-18T01:22:36.500 回答
4

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

于 2012-09-20T22:17:23.337 回答
1

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

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

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

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

于 2009-06-17T18:52:22.993 回答
1

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

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

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

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

也就是说,您最好使用setOrientation:App Store 审批流程并抓住机会;-)

于 2009-06-09T15:38:09.903 回答
1

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

我在控制器中有 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
}
于 2012-08-08T06:57:49.307 回答
0

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

我相信我们拥有的最佳解决方案会复制此错误并让 Apple 知道我们确实希望修复它。我刚刚在错误 ID 8478525 下再次报告了它。

于 2010-09-25T17:01:57.823 回答