如何获取屏幕高度和宽度的方向相关值?

97

我正在尝试以编程方式确定我的应用程序的当前高度和宽度。我使用以下代码:

CGRect screenRect = [[UIScreen mainScreen] bounds];

但是这会产生一个宽度为320和高度为480的结果,无论设备是否处于纵向或横向方向。我该如何确定我的主屏幕的当前宽度和高度(即取决于设备方向)?

12个回答

164

您可以使用类似 UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) 的方法来确定方向,然后相应地使用尺寸。

但是,在 UIViewController 中进行方向更改时...

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
                                 duration:(NSTimeInterval)duration

toInterfaceOrientation中使用传递的方向,因为UIApplication的statusBarOrientation仍将指向旧方向,因为它尚未更改(因为您在will事件处理程序内)。

摘要

有几篇相关的帖子,但每篇都似乎表明您必须:

  1. 查看[[UIScreen mainScreen] bounds]以获取尺寸,
  2. 检查您处于哪个方向,并且
  3. 考虑状态栏高度(如果显示)

链接

工作代码

我通常不这样做,但你引起了我的兴趣。下面的代码应该可以胜任。我写了一个UIApplication类别。我添加了用于获取当前大小或给定方向中的大小的类方法,这是您在UIViewController的willRotateToInterfaceOrientation:duration:中调用的内容。

@interface UIApplication (AppDimensions)
+(CGSize) currentSize;
+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation;
@end

@implementation UIApplication (AppDimensions)

+(CGSize) currentSize
{
    return [UIApplication sizeInOrientation:[UIApplication sharedApplication].statusBarOrientation];
}

+(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation
{
    CGSize size = [UIScreen mainScreen].bounds.size;
    UIApplication *application = [UIApplication sharedApplication];
    if (UIInterfaceOrientationIsLandscape(orientation))
    {
        size = CGSizeMake(size.height, size.width);
    }
    if (application.statusBarHidden == NO)
    {
        size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height);
    }
    return size;
}

@end
要使用该代码,只需调用 [UIApplication currentSize]。同时,我运行了以上代码,所以我知道它可以在所有方向上正确地报告出正确的响应。请注意,我考虑了状态栏。有趣的是,我必须减去状态栏高度和宽度中的最小值。
其他想法
你可以通过查看 UIWindow 的 rootViewController 属性来获取尺寸。我过去曾经研究过这个问题,它同样会在纵向和横向时报告相同的尺寸,但是它会报告旋转变换:
(gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] view] (gdb) po [[[[UIApplication sharedApplication] keyWindow] rootViewController] view] 如果你没有使用任何导航控制器,你可以在主视图下面放置一个 UIView,并使其具有父视图的最大高度/宽度,并随着父视图的变化而变化。然后你可以这样做:[[[[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] subviews] objectAtIndex:0] frame]。这看起来有一行非常强大,但你能理解它的意思。
不过,最好还是在摘要下面执行以上 3 个步骤。开始尝试操作 UIWindows,你会发现奇怪的事情,比如显示 UIAlertView 会将 UIApplication 的 keywindow 改成指向由 UIAlertView 创建的新 UIWindow。谁知道呢?在发现一个依赖于 keyWindow 的错误后,我就知道了它会更改!

5
这似乎是一项非常基本的任务,我很难相信我必须修改自己的代码才能确定这样的事情。 - MusiGenesis
2
我会尝试另一个解决方案,如果有效的话就重新发布。这个网站非常严格,如果你不能快速回答,那么你最好不要回答。哈哈... :D 我希望我的答案至少有所帮助。再次尝试其他方法。 - Sam
1
如果您使用applicationFrame而不是bounds(在UIScreen上),则无需减去状态栏高度。 - aopsfan
1
在竖屏模式下,我得到了错误的尺寸。iOS 版本为 9.0.2。在 iPhone 5s 上,横屏和竖屏模式下,我得到了相同的尺寸 (568.0, 300.0) - AechoLiu
2
从iOS 8开始,mainScreen().bounds.size已成为方向相关的。 - Gerald
显示剩余7条评论

39

这是我的解决方案代码!这种方法可以添加到NSObject类的类别中,或者您可以定义一个顶级自定义UIViewController类,并让所有其他的UIViewControllers都继承它。


-(CGRect)currentScreenBoundsDependOnOrientation
{  

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
    }
    return screenBounds ;
}

注意,自IOS8之后,如Apple Document of UIScreen's bounds property所述:

讨论

此矩形在当前坐标空间中指定,考虑到设备上生效的任何接口旋转。因此,当设备在纵向和横向方向之间旋转时,此属性的值可能会更改。

因此,考虑到兼容性,我们应检测iOS版本并进行以下更改:

#define IsIOS8 (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1)

-(CGRect)currentScreenBoundsDependOnOrientation
{  

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    if(IsIOS8){
        return screenBounds ;
    }
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
    }
    return screenBounds ;
}

基本上和我的答案一样,唯一遗漏的是它是否在倒置的竖屏模式下。不过这只有在你支持该方向时才有影响。 - Robert Wagstaff
2
@Monjer,你不应该给不执行GET请求的方法添加以"get"为前缀的命名。currentScreenBoundsDependOnOrientation是这个方法更好的名称。 - bogen
1
@Hakonbogen,也许你是对的,因为“property”声明会自动生成setter/getter方法,这可能会导致命名冲突,并违反objc的命名约定。感谢您的建议。 - monjer
很高兴苹果公司终于默认承认他们的旋转代码是一团糟,现在只是开始告诉我们当前方向下的该死尺寸。可惜要到第8个版本才实现这一点。 - MusiGenesis

31

这里有一个方便的宏:

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

14

在iOS 8及以上版本中,您应该使用viewWillTransitionToSize:withTransitionCoordinator方法:

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    // You can store size in an instance variable for later
    currentSize = size;

    // This is basically an animation block
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

        // Get the new orientation if you want
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

        // Adjust your views
        [self.myView setFrame:CGRectMake(0, 0, size.width, size.height)];

    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Anything else you need to do at the end
    }];
}

这个方法替代了已弃用的动画方法,该方法没有提供有关大小的信息:

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration

这应该是被接受的答案。现代和最新的。 - SarpErdag
请使用此答案适用于iOS 8及以上版本。 - iPhoneDeveloper

10

iOS 8 及以上版本,屏幕边界的返回值现在已根据当前屏幕方向进行了修正。这意味着在 iPad 横屏模式下,[UIScreen mainScreen].bounds 在 iOS 7 及以下版本上返回值是 768,在 iOS 8 上则为 1024。

以下代码可在所有发布的 iOS 版本上正确返回屏幕高度和宽度。

-(CGRect)currentScreenBoundsDependOnOrientation
{
    NSString *reqSysVer = @"8.0";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
        return [UIScreen mainScreen].bounds;

    CGRect screenBounds = [UIScreen mainScreen].bounds ;
    CGFloat width = CGRectGetWidth(screenBounds)  ;
    CGFloat height = CGRectGetHeight(screenBounds) ;
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;

    if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
        screenBounds.size = CGSizeMake(width, height);
        NSLog(@"Portrait Height: %f", screenBounds.size.height);
    }else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
        screenBounds.size = CGSizeMake(height, width);
        NSLog(@"Landscape Height: %f", screenBounds.size.height);
    }

    return screenBounds ;
}

8

如果您想要视图的方向相关大小,可以直接使用以下代码:

view.bounds.size

太棒了!KISS解决方案 = 保持简单和愚蠢 - bsorrentino
3
KISS并不是“保持简单愚蠢”,哈哈!它的意思是“保持简单,傻瓜!” :-) - Erik van der Neut
此外,显然这个答案只适用于视图已知为全屏的情况。但是,如果是这种情况,那么您可能没有OP发布的原始问题。 - Erik van der Neut

5

我为UIScreen编写了一个类别,它可以在所有iOS版本上工作,因此您可以像这样使用它:
[[UIScreen mainScreen] currentScreenSize]

@implementation UIScreen (ScreenSize)

- (CGSize)currentScreenSize {
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    CGSize screenSize = screenBounds.size;

    if ( NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1 ) {  
        UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        if ( UIInterfaceOrientationIsLandscape(interfaceOrientation) ) {
            screenSize = CGSizeMake(screenSize.height, screenSize.width);
        }
    }

    return screenSize;
}

@end

1
这对我来说看起来是最干净的答案。点赞了。 - Erik van der Neut

5

以下是一种用Swift获取方向相关屏幕尺寸的简便方法:

var screenWidth: CGFloat {
    if UIInterfaceOrientationIsPortrait(screenOrientation) {
        return UIScreen.mainScreen().bounds.size.width
    } else {
        return UIScreen.mainScreen().bounds.size.height
    }
}
var screenHeight: CGFloat {
    if UIInterfaceOrientationIsPortrait(screenOrientation) {
        return UIScreen.mainScreen().bounds.size.height
    } else {
        return UIScreen.mainScreen().bounds.size.width
    }
}
var screenOrientation: UIInterfaceOrientation {
    return UIApplication.sharedApplication().statusBarOrientation
}

这些是我项目中的标准函数:

https://github.com/goktugyil/EZSwiftExtensions

(EZSwiftExtensions是一个包含常用Swift扩展的开源库)

0

使用 -> setNeedsDisplay() 方法来调整你想要改变大小的视图。


0

以下是一些在Swift中提供的答案改进:

    let interfaceOrientation = UIApplication.shared.statusBarOrientation // (< iOS 13)
    let screenSize = UIScreen.main.bounds.size
    let screenWidth = interfaceOrientation.isPortrait ? screenSize.width : screenSize.height
    let screenHeight = interfaceOrientation.isPortrait ? screenSize.height : screenSize.width

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