Custom UIWindows do not rotate correctly in iOS 8

∥☆過路亽.° 提交于 2021-02-06 08:47:11

问题


Get your application into landscape mode and execute the following code:

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [toastWindow removeFromSuperview];
});

In iOS 7 you will get a transparent blue overlay on top of the entire screen that disappears after 5 seconds. In iOS 8 you will get a transparent blue overlay that covers a little over half the screen

device screenshot

This obviously has something to do with the change Apple did in iOS 8 where the screen coordinates are now interface oriented instead of device oriented but in true Apple fashion they seem to have left a myriad of bugs in landscape mode and rotation.

I can "fix" this by checking if the device orientation is landscape and flip the width and height of the main screen bounds but that seems like a horrible hack that is going to break when Apple changes everything again in iOS 9.

CGRect frame = [[UIScreen mainScreen] bounds];

if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
    frame.size.width = frame.size.height;
    frame.size.height = [[UIScreen mainScreen] bounds].size.width;
}

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:frame];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [toastWindow removeFromSuperview];
});

Has anyone been experiencing this problem and come across a better, less brittle solution?

EDIT: I know I could just use a UIView and add it to the key window but I would like to put something on top of the status bar.


回答1:


Your 'fix' isn't great for another reason, in that it doesn't actually rotate the window so that text and other subviews appear with the appropriate orientation. In other words, if you ever wanted to enhance the window with other subviews, they would be oriented incorrectly.

...

In iOS8, you need to set the rootViewController of your window, and that rootViewController needs to return the appropriate values from 'shouldAutoRotate' and 'supportedInterfaceOrientations'. There is some more about this at: https://devforums.apple.com/message/1050398#1050398

If you don't have a rootViewController for your window, you are effectively telling the framework that the window should never autoRotate. In iOS7 this didn't make a difference, since the framework wasn't doing that work for you anyway. In iOS8, the framework is handling the rotations, and it thinks it is doing what you requested (by having a nil rootViewController) when it restricts the bounds of your window.

Try this:

@interface MyRootViewController : UIViewController
@end

@implementation MyRootViewController

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

@end

Now, add that rootViewController to your window after it is instantiated:

UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.rootViewController = [[MyRootViewController alloc]init];



回答2:


I believe you have to convert the UICoordinateSpace.

On iPhone 6 Plus apps can now launch already in landscape orientation, which messed up an application I am working on since it only supports portrait orientation throughout most of the app, except one screen (meaning it needed to support landscape orientations in the plist).

The code that fixed this was as follows:

self.window = [[UIWindow alloc] initWithFrame:[self screenBounds]];

and calculating the bounds with the following code:

- (CGRect)screenBounds
{
    CGRect bounds = [UIScreen mainScreen].bounds;
    if ([[UIScreen mainScreen] respondsToSelector:@selector(fixedCoordinateSpace)]) {
        id<UICoordinateSpace> currentCoordSpace = [[UIScreen mainScreen] coordinateSpace];
        id<UICoordinateSpace> portraitCoordSpace = [[UIScreen mainScreen] fixedCoordinateSpace];
        bounds = [portraitCoordSpace convertRect:[[UIScreen mainScreen] bounds] fromCoordinateSpace:currentCoordSpace];
    }
    return bounds;
}

Hopefully this will lead you in the right direction.




回答3:


In iOS 7 and earlier, UIWindow's coordinate system did not rotate. In iOS 8 it does. I am guessing the frame supplied from [[UIScreen mainScreen] bounds] does not account for rotation, which could cause issues like what you're seeing.

Instead of getting the frame from UIScreen, you could grab the frame from the appDelegate's current key window.

However, it does not appear you really need functionality supplied by UIWindow. I'd like to echo others' recommendation that you use a UIView for this purpose. UIView is more generic than UIWindow, and should be preferred.




回答4:


The best thing to do is use views instead of windows:

Most iOS applications create and use only one window during their lifetime. This window spans the entire main screen [...]. However, if an application supports the use of an external display for video out, it can create an additional window to display content on that external display. All other windows are typically created by the system

But if you think you've got a valid reason to create more than one window, I suggest you create a subclass of NSWindow that handles sizes automatically.

Also note that windows use a lot of RAM, especially on 3x retina screens. Having more than one of them is going to reduce the amount of memory the rest of your application can use before receiving low memory warnings and eventually being killed.



来源:https://stackoverflow.com/questions/26706050/custom-uiwindows-do-not-rotate-correctly-in-ios-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!