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

发布于 2024-09-04 00:14:02 字数 1334 浏览 5 评论 0原文

关于如何实现正确处理横向/纵向模式自动旋转的 iPhone 应用程序,存在很多困惑和一系列相应的问题。当需要以横向模式启动时,实现这样的应用程序尤其困难。最常见的观察到的效果是混乱的布局和不再识别触摸的屏幕区域。

简单搜索标记为 iphonelandscape 的问题即可发现这些问题,这些问题在某些情况下会发生:

已经提出了一组不同的解决方案,其中一些包括通过 CoreGraphics 完全自定义的动画,而其他解决方案则基于从主笔尖加载的第一个视图控制器始终显示正确的观察结果。

我花了大量时间研究这个问题,最终找到了一个解决方案,该解决方案不仅是部分解决方案,而且应该在所有这些情况下都有效。我写这篇 CW 文章的目的是为其他在横向模式下遇到 UIViewControllers 问题的人提供常见问题解答。

请提供反馈并通过纳入任何相关观察结果来帮助提高本文的质量。如果您知道的话,请随意编辑并发布其他/更好的答案。

There has been a lot of confusion and a set of corresponding set of questions here on SO how iPhone applications with proper handling for Landscape/Portrait mode autorotation can be implemented. It is especially difficult to implement such an application when starting in landscape mode is desired. The most common observed effect are scrambled layouts and areas of the screen where touches are no longer recognized.

A simple search for questions tagged iphone and landscape reveals these issues, which occur under certain scenarios:

A set of different solutions have been presented, some of them including completely custom animation via CoreGraphics, while others build on the observation that the first view controller loaded from the main nib is always displayed correct.

I have spent a significant amount of time investigating this issue and finally found a solution that is not only a partial solution but should work under all these circumstances. It is my intend with this CW post to provide sort of a FAQ for others having issues with UIViewControllers in Landscape mode.

Please provide feedback and help improve the quality of this Post by incorporating any related observations. Feel free to edit and post other/better answers if you know of any.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

桃酥萝莉 2024-09-11 00:14:02

文档

在视图控制器中,重写 shouldAutorotateToInterfaceOrientation: 来声明支持的界面方向。每当设备方向发生变化时,控制器基础设施将/应该检查此属性。

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

这是视图控制器需要做的绝对最少的事情。如果您想以横向模式启动应用程序,则需要将以下键添加到 .plist 文件中:

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

Apple 建议以横向右模式启动仅横向应用程序(请参阅 HIG< /a> 用户体验指南 > 立即启动)。

文档中没有的内容:

一点背景知识:

每次您尝试加载与从主笔尖加载的视图控制器不同的视图控制器时,您的视图控制器既不会询问其支持的界面方向,也不会正确设置其框架。只有绑定到窗口的第一个视图控制器才会正确布局。

其他人建议使用连接到主窗口的“MasterViewController”,其他控制器将其视图作为子视图添加到主窗口,而不是直接连接到窗口。虽然我发现这个解决方案是一个可行的选择,但在将模态视图控制器添加到这些子视图的情况下,它无法正常工作。如果您有一些子视图应该能够自动旋转(主控制器将阻止),那么也会出现问题。

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

解决方案:

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

通过 presentModalViewController:animated:UINavigationController 导航堆栈上的任何视图控制器呈现的模态控制器都将旋转并以正确的布局呈现。如果您希望模态视图控制器可旋转到与父视图控制器不同的方向,则需要从 parentshouldAutorotateToInterfaceOrientation 方法返回所需的方向呈现模态视图时的控制器。为了在模态控制器关闭时正确恢复界面方向,您需要在调用 dismissModalViewController:animated: 之前确保 shouldAutorotateToInterfaceOrientation 返回父控制器所需的方向。 。您可以在视图控制器上使用私有BOOL来管理它(例如BOOL isModalMailControllerActive_)。

我很快就会添加一段示例代码,现在已经晚了。如果这篇文章还有任何未解决的问题或有任何不清楚的地方,请告诉我。请随意编辑和改进。

What's in the documentation:

In your view controller, override shouldAutorotateToInterfaceOrientation: to declare your supported interface orientations. This property will/should be checked by the controller infrastructure everytime the device orientation changes.

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

This is the absolute minimum your view controller needs to do. If you want to launch your application in landscape mode, you need to add the following key to your .plist file:

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

Apple recommends starting landscape only applications in Landscape Right mode (see the HIG under User Experience Guidelines > Start Instantly).

What's not in the documentation:

A little background:

Everytime you try to load a different view controller other than that loaded from the main nib, your view controller is neither interrogated about it's supported interface orientations nor is its frame set correctly. Only the first view controller bound to the window will be layed out correctly.

Other people have suggested using a "MasterViewController" hooked up to the main window to which other controllers add their views as subviews instead of hooking directly into the window. While I have found this solutions is a viable option, it does not work correctly in the case of modal view controllers added to those said subviews. There's also a problem if you have some subviews that should be able to autorotate (what the master controller will prevent).

The usage of undocumented API's to force a certain interface orientation is not an option either.

The solution:

The best solution I have found so far is a modification of the "MasterViewController" workaround. Instead of using a custom "MasterViewController", a UINavigationController with hidden Navigation Bar and hidden Tab Bar is used. If all other views are pushed/popped from the navigation stack of this controller, auto-rotations of controllers on that stack will be managed correctly.

Modal controllers presented via presentModalViewController:animated: from any of the view controllers on the UINavigationController's navigation stack will be rotated and rendered with correct layout. If you want your modal view controller to be rotatable to a different orientation than that of the parent view controller, you need to return the desired orientation from the shouldAutorotateToInterfaceOrientation method of the parent controller while the modal view is presented. In order to properly restore the interface orientation when the modal controller is dismissed, you need to make sure shouldAutorotateToInterfaceOrientation returns the desired orientation for the parent controller before you call dismissModalViewController:animated:. You can use a private BOOL on your view controller to manage that (e.g. BOOL isModalMailControllerActive_).

I'll add a piece of sample code soon, It's just to late now. Please let me know if any unresolved issues remain or anything is unclear about this post. Feel free to edit and improve.

荒路情人 2024-09-11 00:14:02

我对 ios 应用程序有一个有趣的要求:

主 viewController 应该只是横向的,但所有其他(可以从主视图推送的)都可以是横向和纵向的。

出现问题 - 当我推送到新的 viewController 时,该视图控制器然后旋转为纵向 - 并且弹出回来 - 主视图不再是横向的。另外 - 打开应用程序,它不是横向的。

为了保持主视图控制器的横向,无论从哪个方向弹出/推入它,我做了以下事情:(在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];

希望它会对某人有所帮助!

PS在 sdk 3.2.5 ios 5.0.1 上测试。

PS 感谢您提供此常见问题解答中的所有信息!

I had an interesting requirement for ios application:

main viewController should be only landscape, but all others (which can be pushed from main) can be landscape and portrait.

Problem occours - when I push to a new viewController, which then is rotated to portraited - and the pop back - main view is no longer landscape. Also - opening application, it is not in landscape.

In order to keep main viewcontroller landscape, no matter from what orientation it was popped/pushed, I did the following thing: (in 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];

Hopefully it will help someone!

P.S.Tested on sdk 3.2.5 ios 5.0.1.

P.S. Thanks for all the info in this FAQ!

怎樣才叫好 2024-09-11 00:14:02

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

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

For the second bullet point, if you want to use pushViewController to go from Portrait-only to Landscape-only view, one simple hack I found is to put the following code into your pushed controller's viewDidLoad:

UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
[viewController release];
你的往事 2024-09-11 00:14:02

我想添加到 Johannes 的答案(使用 UINavigationController 作为 MasterViewController)。

我发现的缺点是新推送到主 VC 导航堆栈上的 ViewController 不会适应任何先前的方向更改。简而言之,堆栈中已有的 VC 会旋转,从它们呈现的模态视图控制器也会旋转,但新添加的 VC 不会旋转。

在找到有效的方法之前,我尝试了很多技巧来解决这个问题。大多数仅适用于pushViewController:动画设置为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];
}

暂时(并且相当不可见)呈现视图控制器允许其接收其方向更新。然后可以将其以正确的方向推送到导航堆栈上。

请告诉我这是否适合您。非常欢迎所有更新和改进!

最后,我强烈推荐约翰内斯的轮换管理方法。

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

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

任何意见都将受到高度赞赏。

I'd like to add to Johannes' answer (using the UINavigationController as MasterViewController).

The disadvantage I have found is that ViewControllers that are newly pushed onto the master VC's navigation stack do not adjust to any prior orientation changes. In short, VCs already on the stack are rotated, modal view controllers presented from them are also rotated, but newly added VCs are not rotated.

I have tried many tricks to fix this before finding one that works. Most only work for pushViewController: with animated set to YES.

To fix the issue entirely, I have subclassed UINagivationController and overriden pushViewController:animated: as follows:

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

Temporarily (and quite invisibly) presenting the view controller allows it to receive its orientation update. It can then be pushed onto the navigation stack in its correct orientation.

Please let me know if this works for you. All updates and improvements are very welcome!

Finally, I highly recommend Johannes' approach to rotation management.

EDIT: Update on popping view controllers from the stack

It seems that all the popViewController-related selectors go crazy when performed with animated:YES. Specifically, the view controller is animated out in the wrong direction. You can use animated:NO and restrict the use of such animations to other UINavigationControllers deeper down in your hierarchy (i.e. the ones that you push onto the root navigation controller's stack).

Any input is greatly appreciated.

蘑菇王子 2024-09-11 00:14:02

我正在开发一个 iPad 应用程序,它在启动时显示一系列项目的垂直滚动图库视图。在横向模式下有 4 个项目。肖像中有三个。当转动 iPad 方向时,它应该刷新图库以使项目整齐地显示在屏幕上。然后,我双击某个项目以深入查看该项目的模式视图。然后我用那个项目做一些事情。最后我放弃了模态视图。

在刷新或方向更改期间,图库视图根据屏幕宽度(或高度)和 UIViewController.interfaceOrientation 的当前方向计算要显示的项目数。

我在使其正常工作时遇到问题。有时,在我关闭模式对话框后,它只会横向显示两个项目。

起初,我使用 UIViewController.view.frame.size 值来计算图库项目的数量。当模式视图被关闭时,该框架大小不正确,例如,即使显示模式对话框时方向没有改变,宽度和高度也已颠倒。

我改用应用程序委托([[UIApplication sharedApplication] delegate]] 并使用 window.frame.size 来计算要显示的图库项目的数量。window.frame.size 在方向更改和模式对话框下保持正确。画廊项目现在可以正确显示。

I'm developing an iPad app that displays vertically scrolling gallery view of an array of items upon startup. In landscape mode there are 4 items across. In portrait there are three. When turning the iPad orientation it is supposed to refresh the gallery to have the items fit neatly across the screen. Then I double tap on an item to drill down to a modal view of that item. Then I do stuff with that item. Finally I dismiss the modal view.

During the refresh or orientation change the gallery view calculates the number of items to display based on the screen width (or height) and the current orientation from UIViewController.interfaceOrientation.

I was having a problem getting this to work correctly. Sometimes it would display only two items in landscape orientation after I dismissed the modal dialog.

At first I was using the UIViewController.view.frame.size values to calculate the number of gallery items. When the modal view was dismissed this frame size was incorrect e.g. the width and height had been reversed even though the orientation had not changed while the modal dialog was displayed.

I switched to using the application delegate ([[UIApplication sharedApplication] delegate]] and taking the window.frame.size to calculate the number of gallery items to display across. The window.frame.size stays correct under orientation changes and modal dialogs. The gallery items display correctly now.

仄言 2024-09-11 00:14:02

这会起作用...

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

This will work...

UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    UIView *view = [window.subviews objectAtIndex:0];
    [view removeFromSuperview];
    [window addSubview:view];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文