iPhone横屏常见问题及解决方案

18
在SO上,有许多关于如何实现具有正确横/竖屏模式自动旋转处理的iPhone应用程序的困惑和相应的一系列问题。当希望在横屏模式下启动应用程序时,特别难以实现这样的应用程序。最常见的观察到的效果是布局混乱和屏幕上不再被识别的触摸区域。
简单搜索标记为iphonelandscape的问题会揭示以下情况: 已经提出了一组不同的解决方案,其中一些包括通过CoreGraphics进行完全自定义的动画,而其他解决方案则基于观察到从主nib加载的第一个视图控制器始终显示正确。

我花费了大量时间调查这个问题,最终找到了一个解决方案,不仅是部分解决方案,而且应该在所有情况下都适用。我的意图是通过这篇 CW 帖子为其他在横屏模式下遇到问题的 UIViewControllers 提供一些常见问题解答。

请提供反馈并帮助改善此帖子的质量,包括任何相关观察结果。如果您知道其他/更好的答案,请随意编辑和发布。


标签栏控制器横屏模式 - 不确定这是否是另一个经常被问到的问题,但如果它在上面的列表中就可以省下我几个小时了。 - herzbube
6个回答

13

文档中包含什么内容:

在您的视图控制器中,重写shouldAutorotateToInterfaceOrientation:方法以声明您支持的界面方向。每次设备方向更改时,控制器基础架构都会检查此属性。

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
   return  (orientation == UIInterfaceOrientationLandscapeRight);
}

这是视图控制器必须完成的绝对最小要求。如果您想在横向模式下启动应用程序,您需要将以下关键字添加到您的.plist文件中:

<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>

苹果建议在“用户体验指南”中的“立即启动”下,将仅横屏的应用程序设置为“横向右侧模式”(请参阅HIG)。

文档中没有提到的内容:

一些背景知识:

每次尝试加载除从主nib加载的视图控制器以外的其他视图控制器时,都不会询问您的视图控制器关于其支持的界面方向,也不会正确设置其框架。只有绑定到窗口的第一个视图控制器才能正确布局。

其他人建议使用连接到主窗口的“MasterViewController”,其中其他控制器将其视图添加为子视图,而不是直接连接到窗口。虽然我发现这个解决方案是可行的选择,但对于添加到这些子视图中的模态视图控制器,它不起作用。如果您有一些子视图应该能够自动旋转(主控制器将防止此操作),则还存在问题。

强制特定界面方向的未记录API的使用也不是选项。

解决方案:

我迄今为止找到的最佳解决方案是“MasterViewController”解决方法的修改。使用隐藏导航栏和隐藏选项卡栏的 UINavigationController 来替代使用自定义的“MasterViewController”。如果所有其他视图从该控制器的导航堆栈中推送/弹出,则该堆栈上的控制器的自动旋转将得到正确管理。

通过presentModalViewController:animated:UINavigationController导航栏堆栈中的任何视图控制器呈现的模态控制器将旋转并呈现正确的布局。如果您希望您的模态视图控制器旋转到与父视图控制器不同的方向,则需要在模态视图呈现时从控制器的shouldAutorotateToInterfaceOrientation方法返回所需的方向。为了在模态控制器被解除显示时正确恢复界面方向,您需要确保在调用dismissModalViewController:animated:之前,shouldAutorotateToInterfaceOrientation返回父控制器所需的方向。您可以使用一个私有的BOOL来管理您的视图控制器(例如:BOOL isModalMailControllerActive_)。
我很快会添加一段示例代码,只是现在太晚了。如果此帖子仍有未解决的问题或任何不清楚的地方,请告诉我。请随意编辑和改进。

谢谢!这对我有很大帮助。不幸的是,只有当视图控制器被呈现时旋转时,栈上的视图控制器才会被旋转。如果它们在旋转发生后添加,则它们的方向不会正确更改。当使用animated:YES推动时,您可以手动调用willRotateToInterfaceOrientation:duration和didRotateFromInterfaceOrientation来解决此问题。然而,使用animated:NO则无效。我已经尝试了我能想到的一切。有什么建议吗?解决方案,解决方法? - Timo
1
从iOS8开始,此方法(shouldAutorotateToInterfaceOrientation)已被弃用-现在您应该尝试:preferredInterfaceOrientationForPresentation-希望这能帮助到某些人! - tony.stack

7

我有一个关于iOS应用的有趣需求:

主视图控制器应该只能横屏显示,但所有其他可以从主视图推出的视图控制器可以横屏或竖屏显示。

问题出现在当我推出一个新的视图控制器并将其旋转为竖屏时,然后再弹回时,主视图不再是横屏显示。此外,在打开应用程序时,它也不是横屏显示。

为了保持主视图控制器始终横屏显示,无论从哪个方向弹出/推出,我做了以下操作:(在viewWillAppear中)

//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];

//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[[UIViewController alloc] init] autorelease];
[self presentModalViewController:mVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

希望能对某些人有所帮助!

附注:在sdk 3.2.5 ios 5.0.1上测试通过。

附注:感谢此常见问题解答中的所有信息!


3

对于第二个要点,如果您想使用pushViewController从仅支持竖屏的视图切换到仅支持横屏的视图,我发现一个简单的技巧是将以下代码放入推送控制器的viewDidLoad中:

UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
[viewController release];

2
我想补充一下Johannes的回答(使用UINavigationController作为MasterViewController)。
我发现的缺点是,新推入主VC导航栈中的ViewControllers不会调整到任何先前的方向更改。简而言之,已经在堆栈上的VC被旋转,从它们呈现的模态视图控制器也被旋转,但是新添加的VC没有被旋转。
在找到一个解决方法之前,我尝试了许多技巧来修复这个问题。大多数只适用于pushViewController:与animated设置为YES。
为了完全解决这个问题,我子类化了UINagivationController并覆盖了pushViewController:animated:如下:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // Correctly autoOrient the given view controller
    [self presentModalViewController:viewController animated:NO];
    [self dismissModalViewControllerAnimated:NO];

    // Push it as normal (now in its correct orientation)
    [super pushViewController:viewController animated:animated];
}

临时(并且相当隐形)展示视图控制器允许其接收方向更新。然后,它可以在正确的方向上推入导航栈中。

如果这对您有用,请告诉我。所有更新和改进都非常受欢迎!

最后,我强烈推荐 Johannes 的旋转管理方法。

编辑:有关从堆栈中弹出视图控制器的更新

似乎所有与popViewController相关的选择器在使用animated:YES时都会变得疯狂。具体来说,视图控制器以错误的方向进行动画处理。您可以使用animated:NO,并将此类动画的使用限制在层次结构中更深入的其他UINavigationControllers中(即将其推入根导航控制器的堆栈中的那些控制器)。

非常感谢任何意见。


感谢您的输入。需要注意的是,FAQ是根据iOS 3.1或3.2编写的,因此自那时以来许多事情可能已经发生了变化。我从未遇到过您描述的问题(也没有检查过您的解决方法是否有效),但很高兴您提供它作为帮助他人的参考!谢谢。 - Johannes Rudolph
@Johannes:感谢您的回复!您是否从未遇到此问题,因为您在横向方向上没有推送新的视图控制器?否则,如果您有,请给我一些建议,如何以正确的方向这样做?(请参见我的编辑,了解我为什么正在寻找不同的解决方案。) - Timo
据我所记,我只推过两个视图控制器,一个是“主控制器”,它在“方向控制器”中显示我的应用程序内容,然后按需呈现一个MFMailComposeViewController作为模态对话框(这很好地工作并且可以正确旋转)。您使用的是哪个iOS版本? - Johannes Rudolph
请参考此答案:https://dev59.com/KnVC5IYBdhLWcg3wykej#4035140 使用UIWindow视图移除方法可以获得正确的navigationController推入/弹出动画。 - Rik Smith-Unna
还要注意的是,在iOS6中,视图控制器动画方向问题已经得到解决。 - Rik Smith-Unna

2
我正在开发一款iPad应用程序,启动时会显示一个垂直滚动的图库视图,其中包含一系列项目。在横屏模式下,每行显示4个项目。在竖屏模式下,则显示3个。当旋转iPad方向时,应该刷新图库以使项目整齐地适应屏幕。然后我双击一个项目,进入该项目的模态视图。接着我对该项目进行一些操作,最后关闭模态视图。
在刷新或旋转方向期间,图库视图会根据UIViewController.interfaceOrientation和屏幕宽度(或高度)计算要显示的项目数量。
我曾经遇到过一个问题,就是有时在关闭模态对话框后,横屏模式下只会显示两个项目。
起初,我使用UIViewController.view.frame.size值来计算图库项目的数量。但是,当关闭模态视图时,这个frame size会变成不正确的值,例如,即使在模态对话框显示时方向没有改变,宽度和高度也会颠倒。因此,我改为使用应用程序委托对象([[UIApplication sharedApplication] delegate]])并获取window.frame.size来计算要显示的图库项目数量。在旋转方向和模态对话框下,window.frame.size保持正确。现在图库项目正确地显示了。

-3

这将起作用...

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接