在iOS8中,[UIScreen mainScreen].bounds.size是否会根据设备方向而改变?

186

我在iOS 7和iOS 8中运行了以下代码:

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
BOOL landscape = (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight);
NSLog(@"Currently landscape: %@, width: %.2f, height: %.2f", 
      (landscape ? @"Yes" : @"No"), 
      [[UIScreen mainScreen] bounds].size.width, 
      [[UIScreen mainScreen] bounds].size.height);

以下是iOS 8的结果:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 568.00, height: 320.00

与iOS 7中的结果相比:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 320.00, height: 568.00

有没有文档说明这个更改?还是iOS 8 APIs中的临时错误?


13
iOS8的输出似乎更加合乎逻辑。 - Levi
18个回答

174

是的,在iOS8中,这是与方向有关的,而不是一个错误。您可以查看WWDC 2014的会话214以获取更多信息:"iOS 8中的视图控制器进展"

演示文稿中的引用:

UIScreen现在是面向界面的:

  • [UIScreen bounds]现在是面向界面的
  • [UIScreen applicationFrame]现在是面向界面的
  • 状态栏框架通知是面向界面的
  • 键盘框架通知是面向界面的

7
在上述的WWDC演讲中,界面导向部分从51:50开始。 - cnotethegr8
3
我今天大部分时间都试图克服这个问题。显然它并不总是发生,因此如果你是需要在其他应用程序中工作的SDK开发人员,这件事情就变得更加复杂了。 - Glenn Maynard
1
@aroth,如果不打破一些旧习惯,就无法创新。 - Boris Y.
24
@borisy - 我非常尊重地不同意您的观点。在大多数情况下,我们可以在保留向后兼容性的同时进行创新。在我看来,破坏性变更更像是懒惰而不是其他任何事情。他们希望“每个人”都去做这项工作。 - aroth
1
如果解释一下什么是“面向接口编程”,这个答案会更有帮助。我可能错了,但我认为方向依赖性只涉及视图控制器。如果你取任何其他类并计算边界,它们仍然按照旧样式,总是纵向的。 - Maxi Mus
显示剩余3条评论

60

是的,在iOS8中它是有方向性的。

我编写了一个实用程序方法,解决了对需要支持旧版本操作系统的应用程序的此问题。

+ (CGSize)screenSize {
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        return CGSizeMake(screenSize.height, screenSize.width);
    }
    return screenSize;
}

1
我修改了代码,请检查,你检查操作系统版本的条件是错误的。 - Jageen
我理解的是,如果操作系统是iOS8且方向为横屏,我们需要更改高度和宽度,对吗? - Jageen
@Jageen 不好意思,iOS 8中该方法是依赖于方向的。你的逻辑是错误的。你的编辑已经被正确拒绝了。 - cbartel
@cbartel有没有一种方法可以将其分类到UIScreen中,以便我们对[UIScreen mainScreen].bounds.size的原始调用适用于iOS 7和iOS 8? - kevinl
如何在应用程序扩展中进行检查?没有访问[UIApplication sharedApplication]的权限。 - barfoon
显示剩余4条评论

35

确实,iOS 8中屏幕大小现在取决于方向。然而,有时希望得到一个固定于竖向方向的尺寸。以下是我的做法。

+ (CGRect)screenBoundsFixedToPortraitOrientation {
    UIScreen *screen = [UIScreen mainScreen];

    if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                    return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
    } 
    return screen.bounds;
}

2
我发现这个方法可以解决常规 iPhone 应用程序的问题,但无法解决在 iPad 上运行的 iPhone 应用程序的问题。 - ThomasW

34

是的,现在它取决于方向。

我更喜欢下面这种以独立于方向的方式获取屏幕大小的方法,而不是上面一些答案中提到的方法,因为它更简单,而且不依赖任何方向代码(其状态可能取决于调用它们的时间)或版本检查。 如果您需要它稳定地运行在所有iOS版本上,那么即使您想要新的iOS 8行为,它也将起作用。

+(CGSize)screenSizeOrientationIndependent {
     CGSize screenSize = [UIScreen mainScreen].bounds.size;
     return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
}

1
但在 Apple TV 等几乎总是宽于高的设备上可能无法正常工作。 - cbh2000

11

与这个问题相关,因为它解决了我的问题,这里有两个定义我用于屏幕宽度和高度的计算:

#define SCREEN_WIDTH (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height) : [[UIScreen mainScreen] bounds].size.width)

#define SCREEN_HEIGHT (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width) : [[UIScreen mainScreen] bounds].size.height)

#define IOS_VERSION_LOWER_THAN_8 (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
如果您同时支持iOS 7和iOS 8,这是解决此问题的最佳方案。

@Namit Gupta,你的编辑是错误的。如果IOS版本为8或以上,我们可以使用UIScreen尺寸,因为这些尺寸是方向相关的。在iOS 8之前,您必须检查状态栏方向。你的编辑现在说所有不低于8的iOS版本都应该检查状态栏方向,这是错误的。 - Antoine

10
你可以使用 nativeBounds (与方向无关)。 nativeBounds

The bounding rectangle of the physical screen, measured in pixels. (read-only)

Declaration SWIFT

  var nativeBounds: CGRect { get }

This rectangle is based on the device in a portrait-up orientation. This value does not change as the device rotates.

检测设备的高度:
if UIScreen.mainScreen().nativeBounds.height == 960.0 {

}

检测设备的宽度:

if UIScreen.mainScreen().nativeBounds.width == 640.0 {

}

4
请注意,这种方法返回像素,而大多数其他方法处理的是点。这是两个非常不同的概念。 - jcsahnwaldt Reinstate Monica
1
在我的情况下,这正是我所需要的 :) (OpenGL或其他不基于UIView但我需要知道精确像素尺寸的视图) - Bersaelor
4
警告:在 iPhone 6+ 上返回的是 1080 x 1920,可能不符合您的要求。 - Chanon

9
这不是iOS 8 SDK的bug。他们使边界接口取决于方向。关于您提出的一些参考或文档的问题,我强烈建议您观看“iOS 8中的视图控制器进阶”视频,它是WWDC 2014的第214个会话。最有趣的部分(根据您的疑虑)是从50:45开始的“屏幕坐标”。

5

是的,在iOS8中它与方向有关。

以下是如何以一致的方式在SDK和OS版本中使用iOS 8风格读取边界。

#ifndef NSFoundationVersionNumber_iOS_7_1
# define NSFoundationVersionNumber_iOS_7_1 1047.25
#endif

@implementation UIScreen (Legacy)

// iOS 8 way of returning bounds for all SDK's and OS-versions
- (CGRect)boundsRotatedWithStatusBar
{
    static BOOL isNotRotatedBySystem;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        BOOL OSIsBelowIOS8 = [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0;
        BOOL SDKIsBelowIOS8 = floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1;
        isNotRotatedBySystem = OSIsBelowIOS8 || SDKIsBelowIOS8;
    });

    BOOL needsToRotate = isNotRotatedBySystem && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
    if(needsToRotate)
    {
        CGRect screenBounds = [self bounds];
        CGRect bounds = screenBounds;
        bounds.size.width = screenBounds.size.height;
        bounds.size.height = screenBounds.size.width;
        return bounds;
    }
    else
    {
        return [self bounds];
    }
}

@end

1
检查SDK版本也是一个好的提示,以防同事使用不同版本的SDK构建您的应用程序(尽管这不应该发生!)。 - Craig McMahon

4

我的解决方案是MaxK和hfossli的结合体。我将这个方法制作成了UIScreen的一个类别,并且没有版本检查(这是一种不好的做法):

//Always return the iOS8 way - i.e. height is the real orientation dependent height
+ (CGRect)screenBoundsOrientationDependent {
    UIScreen *screen = [UIScreen mainScreen];
    CGRect screenRect;
    if (![screen respondsToSelector:@selector(fixedCoordinateSpace)] && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        screenRect = CGRectMake(screen.bounds.origin.x, screen.bounds.origin.y, screen.bounds.size.height, screen.bounds.size.width);
    } else {
        screenRect = screen.bounds;
    }

    return screenRect;
}

4
下面的方法可以用于查找给定方向的屏幕边界,与iOS版本无关。该方法将根据设备的屏幕大小返回边界,并且将独立于iOS版本给出相同的CGRect值。
- (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation {

    CGFloat width   = [[UIScreen mainScreen] bounds].size.width;
    CGFloat height  = [[UIScreen mainScreen] bounds].size.height;

    CGRect bounds = CGRectZero;

    if (UIInterfaceOrientationIsLandscape(orientation)) {
        bounds.size = CGSizeMake(MAX(width, height), MIN(width, height));
    } else {
        bounds.size = CGSizeMake(MIN(width, height), MAX(width, height));
    }

    return bounds;
}

// For the below example, bounds will have the same value if you run the code on iOS 8.x or below versions.
CGRect bounds = [self boundsForOrientation:UIInterfaceOrientationPortrait]; 

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