iPhone - 根据当前方向旋转UIWindow?

31

我正在将一个额外的UIWindow添加到我的应用程序中。

我的主窗口可以正确旋转,但是我添加的这个额外窗口却无法进行旋转。

如何根据当前设备方向旋转UIWindow是最佳方式?


2
为什么要添加另一个UIWindow? - Moshe
8
每个 iOS 应用程序都需要至少一个窗口——即 UIWindow 类的一个实例,有些应用程序可能会包含多个窗口。 - titaniumdecoy
这并没有回答为什么有人会想要另一个窗口的问题。最常见的用例似乎是用于视频输出。 - Danilo Campos
很迷人。但我看了问题。我是在回应莫西。 - Danilo Campos
5
@Moshe 在尝试将内容“置于”UIPopoverController上方(UIPopoverController本身创建了一个单独的窗口)时,创建另一个窗口是一种常见的技术。从UIPopoverController进行拖放是经典示例。 - Jaysen Marais
5个回答

45
你需要为UIWindow自行编写代码。 监听UIApplicationDidChangeStatusBarFrameNotification通知,然后在状态栏更改时设置变换。 你可以从-[UIApplication statusBarOrientation]中读取当前方向,并像这样计算变换:
#define DegreesToRadians(degrees) (degrees * M_PI / 180)

- (CGAffineTransform)transformForOrientation:(UIInterfaceOrientation)orientation {

    switch (orientation) {

        case UIInterfaceOrientationLandscapeLeft:
            return CGAffineTransformMakeRotation(-DegreesToRadians(90));

        case UIInterfaceOrientationLandscapeRight:
            return CGAffineTransformMakeRotation(DegreesToRadians(90));

        case UIInterfaceOrientationPortraitUpsideDown:
            return CGAffineTransformMakeRotation(DegreesToRadians(180));

        case UIInterfaceOrientationPortrait:
        default:
            return CGAffineTransformMakeRotation(DegreesToRadians(0));
    }
}

- (void)statusBarDidChangeFrame:(NSNotification *)notification {

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

    [self setTransform:[self transformForOrientation:orientation]];

}

根据您的窗口大小,您可能需要更新框架。


@morten-fast 感谢您的提示。但是我无法弄清如何在旋转后处理坐标系... x 变成了 ywidth 变成了 height 吗?还是没有变化? - oleynikd
好的。我已经做了一些研究,似乎应用程序的主要 UIWindow 处理方向更改而不需要旋转...那么还有其他方法吗?有人知道吗? - oleynikd
1
在iOS8及以上版本中,如果您将窗口的rootViewController设置为正在呈现的UIViewController,则它将为您处理旋转(尽管我在此方面遇到了一些错误 - 特别是在启动应用程序的视图控制器上,如果我弹出键盘)。 - Ser Pounce

18

只需创建一个带有自己 UIViewUIViewController,将其分配为您的窗口的 rootViewController,并将所有进一步的UI添加到控制器的视图中(而不是直接添加到窗口中),控制器会为您处理所有旋转:

UIApplication * app = [UIApplication sharedApplication];
UIWindow * appWindow = app.delegate.window;

UIWindow * newWindow = [[UIWindow alloc] initWithFrame:appWindow.frame];
UIView * newView = [[UIView alloc] initWithFrame:appWindow.frame];
UIViewController * viewctrl = [[UIViewController alloc] init];

viewctrl.view = newView;
newWindow.rootViewController = viewctrl;

// Now add all your UI elements to newView, not newWindow.
// viewctrl takes care of all device rotations for you.

[newWindow makeKeyAndVisible];
// Or just newWindow.hidden = NO if it shall not become key
当然,可以在界面构建器中创建完全相同的设置,而不需要编写任何代码(除了在显示窗口之前将框架大小设置为填充整个屏幕)。
当然,使用界面构建器也可以创建完全相同的设置,而不需要编写任何代码(除了在显示窗口之前将框架大小设置为填充整个屏幕)。

只是好奇,为什么您建议将所有子视图添加到newView而不是newWindow? - Ser Pounce
因为旋转是由ViewController(viewctrl)执行的,而且这个ViewController只旋转它控制的视图(即newView),然后递归地旋转所有子视图。如果您直接将它们添加到newWindow中,则您的视图不会被ViewController旋转,您将不得不手动再次旋转它们(当您旋转设备时,UIWindow不会旋转任何视图,甚至不会旋转自身)。 - Mecki

4
你需要设置新窗口的rootViewController,然后窗口的子视图将正确旋转。
myNewWindow!.rootViewController = self

然后你可以在旋转方法中改变框架。

例如(iOS8中的Swift)

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { customAlertView.frame = UIScreen.mainScreen().bounds }


1

我不知道你是否对窗口进行任何操作。但是根控制器需要以YES响应shouldAutorotate。


谢谢,但那个方法只适用于UIVievController,我正在尝试旋转一个UIWindow。 - aryaxt

1
你可以为你的UIWindow设置rootController。例如:
fileprivate(set) var bottonOverlayWindow = UIWindow()

self.bottonOverlayWindow.rootViewController = self; 

// 'self'表示您已添加UIWindow视图的ViewController。因此,每当您的ViewController更改方向时,窗口视图也会更改其方向。

如果遇到任何问题,请告诉我。


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