UINavigationController和autorotation

时间:2009-06-09 14:45:08

标签: iphone cocoa-touch uinavigationcontroller rotation

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

第一个视图拒绝旋转(按预期方式)。按下第二个视图后,用户可以旋转设备,UI将旋转(也按预期)。如果第二个视图处于横向模式且用户按下后退按钮(调用popViewControllerAnimated:),则第一个视图将显示为旋转(意外!)。

如果用户将设备旋转回纵向,视图将旋转,然后像以前一样陷入纵向模式。这可行,但对于用户来说,它们会旋转回来之前很难看。所以我正在寻找一种让这种观点保持纵向模式的方法。

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

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

7 个答案:

答案 0 :(得分:6)

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

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

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

E.g。以下代码将在支持所有方向但其父级仅支持格局的视图控制器中使用:

- (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);
}

答案 1 :(得分:5)

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

目标: UINavigationController和其堆栈中的大多数viewcontrollers都固定在纵向,除了堆栈中的一个viewcontroller允许旋转为纵向和横向。

问题: 直觉上我通过检查topViewController是否是rotableViewController来设置一个选择性的shouldAutorotateToInterfaceOrientation。但是,在横向模式下从rotableViewController弹出后,导航控制器现在以横向模式显示,但不允许。

解决方案:杀手是禁止viewWillAppear处的旋转并且呈现&解除没有动画的modalViewController。

  1. appViewController作为主机添加到窗口中 viewController,即RootViewController的rooter;
  2. 使用导航控制器添加到appViewController 委托设置为appViewController;
  3. 在AppViewController

  4. - (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];
        }
    }
    

答案 2 :(得分:4)

iOS 5增加了+ [UIViewController attemptRotationToDeviceOrientation],它解决了我的问题。

答案 3 :(得分:1)

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

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

  1. 配置虚拟导航栏以与传出导航控制器完全匹配,并将其直接放置在导航控制器栏上。 (您可以复制当前视图控制器的UINavigationItem的配置并将其推送)
  2. 将新导航控制器放在屏幕右边缘
  3. 动画新旧控制器框架从右向左移动
  4. 为传入的视图控制器创建UINavigationItem的副本,并将其推送到虚拟导航栏
  5. 动画完成后,从视图中删除虚拟UINavigationBar,以及传出导航控制器。
  6. 所有这些都是很多工作,但如果你非常聪明(而且非常顽强),你或许可以让它发挥作用。我很想看到结果!

    也就是说,您可能最好只使用setOrientation:并利用App Store批准流程的机会; - )

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:1)

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

我在控制器中有2个视图。根视图仅支持LandscapeRight,第二支持LandscapeRightPortrait

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

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

变通方法本身包含在Root视图源中 现在根视图根据代码旋转,但用户无法看到它。

//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
}

答案 6 :(得分:0)

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

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