UINavigationController和autorotation

问题描述:

我有一个UIViewController,在shouldAutorotateToInterfaceOrientation:中为UIDeviceOrientationPortraitNO返回YES。在堆栈顶部显示该视图时,我使用pushViewController:animated:推送新的UIViewController。对于shouldAutorotateToInterfaceOrientation:中的任何内容,新控制器将返回YESUINavigationController和autorotation

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

如果用户将设备旋转回纵向,视图将旋转,然后像以前一样卡入纵向模式。这可以工作,但用户直到他们旋转后才会变丑。所以我正在寻找一种方法让这个视图保持纵向模式。

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

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

+0

我相信有应用程序的例子(大概)在应用程序商店中使用setOrientation。一个是Tweetie,它可以在写推文时强制风景键盘。也许他们逃避了它,因为它不是默认设置。 – 2009-06-09 15:50:50

+0

现在是2014年,这个问题仍然发生在ios 8上。有没有人找到真正的答案,但这个问题看起来不像黑客? – 2014-10-31 16:17:59

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

你必须修补儿童导航控制器以拥有正确的后退按钮。当然,你必须处理后退按钮。您可能必须使用虚拟UINavigationBar才能从一个导航控制器执行动画,以便转换看起来正确。您还必须为导航控制器之间的“推送”过渡设置动画,这可能需要稍微调整以使其看起来正确。您将不得不:

  1. 配置一个虚拟导航栏,使其与导航控制器完全匹配,并将其直接放置在导航控制器栏的顶部。在右边缘
  2. (您可以复制当前视图控制器的UINavigationItem的配置,并推动它)
  3. 将新的导航控制器关闭屏幕从右到左的动画新老控制器的帧的运动
  4. 用于输入视图控制器创建UINavigationItem的副本,并推动它虚设导航栏
  5. 当在动画完成上,从视图中去除伪UINavigationBar,并且还传出导航控制器。

所有这些都是很多工作,但是如果你非常聪明(而且非常坚韧),那么你可能会得到它的工作。我很想看到结果!

这就是说,你可能会更好只使用setOrientation:,并采取与App Store的审批流程;-)

与3.0 OS再试一次(现在我们可以谈论它),你的机会。这两种特殊情况已经在3.0摸索出:

  1. 呈现肖像模式的看法在横向视图,反之亦然。一个例子是邮件中用于查看附件的3.0版之前的行为。您可以旋转以横向查看PDF并在取消附件视图时恢复消息的纵向视图。 (现在我们在3.0中已经有了横向消息视图,这种行为似乎已经消失了)。

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

似乎有一些棘手的审美问题,如何默认过渡所有的排列应该看起来。如果你用慢镜头查看,有一些奇怪的元素,但它会在你自己的代码中向后弯曲。

+0

我试过这个,#1似乎工作正常,但#2不适合我。我使用表格视图控制器设置了一个导航控制器项目。根视图不能旋转,但推送的视图可以。当你弹出第二个视图时,它将保持旋转状态。实际上,导航栏项目似乎很困惑,甚至在弹出它后显示第二个视图的标题。 – 2010-02-01 04:36:32

所以这个恼人的错误是一个很难很长的路要走,从iOS的1到iOS 4

我相信我们有它复制this bug,让苹果知道我们真的希望它固定的最佳解决方案。我刚刚在bug ID 8478525下再次报告了它。

这是一篇旧帖子,但是因为它尚未解决。我想分享我的解决方案,对于任何其他可能头痛的人。

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

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

解决方案:杀手将禁止旋转viewWillAppear,并且当前&关闭一个没有动画的modalViewController。

  1. appViewController作为主机 viewController添加到窗口中,即,Rooter比RootViewController;
  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]; 
    } 
} 

到的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); 
} 
+0

肮脏的肮脏的黑客,但它的作品。 – beefon 2012-01-10 04:40:15

+2

在iOS 7上不适用于我。但是,删除窗口的rootViewController并将其放回原处。只要把它放在那里,以防有人遇到同样的问题。 – entropy 2013-09-16 08:05:51

我发现这个问题一个很好的解决方法。线索是支持所有方位为所有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 
} 

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

+1

根据文档(“尝试将所有窗口旋转到设备的方向”),似乎这是针对反向用例的,即您推送一个新的视图控制器,支持前一个不支持的界面方向。 – Koraktor 2013-01-04 18:40:47