[UIScreen mainScreen].bounds.size 在 iOS8 中是不是变得依赖于方向?
Posted
技术标签:
【中文标题】[UIScreen mainScreen].bounds.size 在 iOS8 中是不是变得依赖于方向?【英文标题】:Is [UIScreen mainScreen].bounds.size becoming orientation-dependent in iOS8?[UIScreen mainScreen].bounds.size 在 iOS8 中是否变得依赖于方向? 【发布时间】:2014-07-31 18:27:19 【问题描述】:我在 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 API 中的一个临时错误?
【问题讨论】:
嗯,iOS8 的输出看起来更合乎逻辑 【参考方案1】:是的,它在 iOS8 中取决于方向,而不是错误。您可以查看 WWDC 2014 的第 214 场会议以获取更多信息:"View Controller Advancements in iOS 8"
演讲中的引述:
UIScreen 现在是面向界面的:
[UIScreen 边界] 现在面向界面 [UIScreen applicationFrame] 现在面向界面 状态栏框架通知是面向界面的 键盘框架通知是面向界面的【讨论】:
在上面提到的WWDC讲座中,面向界面的部分从51:50开始。 我今天大部分时间都在努力解决这一进步。显然它不会总是发生,因此,如果您是需要在其他应用程序中工作的 SDK 的开发人员,那么这些事情就会变得更加复杂。 @aroth 如果不打破一些旧习惯,你就无法创新。 @borisy - 我不同意。在大多数情况下,它可以在保持向后兼容性的同时进行创新。在我看来,突破性的变化比其他任何事情都更带有懒惰的味道。他们希望其他人来做这项工作。 如果解释了面向接口的真正含义,这个答案会更有帮助。我可能错了,但我认为方向依赖只与视图控制器有关。如果你采取任何其他类并计算边界,它们仍然按照旧样式,总是纵向。【参考方案2】:是的,它在 iOS8 中取决于方向。
我为需要支持旧版本操作系统的应用编写了一个 Util 方法来解决此问题。
+ (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;
【讨论】:
我编辑了代码请检查,您检查操作系统版本的条件错误 @Jageen 请澄清,版本检查对我有用。有什么问题? 我的理解是,如果操作系统是 iOS8 并且方向是横向,我们需要更改高度和宽度,对吗? @Jageen Negative,在 iOS 8 中,该方法取决于方向。你的逻辑被切换了。您的修改被正确拒绝了。 @cbartel 有没有办法将其归类为 UIScreen,以便我们对 [UIScreen mainScreen].bounds.size 的原始调用适用于 iOS 7 和 iOS 8?【参考方案3】:是的,确实,屏幕尺寸现在在 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;
【讨论】:
我发现这可以解决常规 iPhone 应用程序的问题,但不能解决在 iPad 上运行的 iPhone 应用程序的问题。【参考方案4】:是的,它现在取决于方向。
我更喜欢以下以与方向无关的方式获取屏幕尺寸的方法,而不是上面的一些答案,因为它更简单,而且它不依赖于任何方向代码(其状态可以是取决于调用它们的时间)或版本检查。您可能需要新的 iOS 8 行为,但如果您需要它在所有 iOS 版本上保持稳定,这将起作用。
+(CGSize)screenSizeOrientationIndependent
CGSize screenSize = [UIScreen mainScreen].bounds.size;
return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
【讨论】:
可能不适用于 Apple TV 之类的设备,因为它(几乎)总是比高度更宽。【参考方案5】:与这个问题相关,因为它解决了我的问题,这里有两个定义我用于屏幕宽度和高度计算:
#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 版本都应该检查状态栏方向,这是错误的。【参考方案6】:您可以使用nativeBounds
(与方向无关)
原生边界
物理屏幕的边界矩形,以像素为单位。 (只读)
SWIFT 声明
var nativeBounds: CGRect get
此矩形基于纵向向上的设备。这 值不会随着设备旋转而改变。
检测设备的高度:
if UIScreen.mainScreen().nativeBounds.height == 960.0
检测设备的宽度:
if UIScreen.mainScreen().nativeBounds.width == 640.0
【讨论】:
请注意,此方法返回像素,而大多数其他方法处理点。非常不同的东西。 在我的情况下正是我需要的 :) (OpenGL 或其他不基于 UIViews 但我需要知道确切像素尺寸的视图) 警告:在 iphone 6+ 上返回 1080 x 1920,这可能不是你想要的【参考方案7】:这不是 iOS 8 SDK 中的错误。 他们使边界界面方向取决于。根据您对某些参考或文档的问题,我强烈建议您观看View Controller Advancements in iOS 8
这是来自WWDC 2014 的214 会话。最有趣的部分(根据您的疑问)是Screen Coordinates
,从 50:45 开始。
【讨论】:
【参考方案8】:是的,它在 iOS8 中取决于方向。
以下是如何以 iOS 8 方式在 SDK 和 OS 版本中以一致的方式读取边界的方法。
#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
【讨论】:
检查 SDK 版本也是一个很好的提示,以防同事使用不同版本的 SDK 构建您的应用程序(尽管这不应该发生!)。【参考方案9】:我的解决方案是 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;
【讨论】:
【参考方案10】:我需要一个快速帮助函数,在 iOS8 下保持与 iOS7 相同的行为 - 这让我可以替换我的 [[UIScreen mainScreen] bounds]
调用,而无需触及其他代码...
+ (CGRect)iOS7StyleScreenBounds
CGRect bounds = [UIScreen mainScreen].bounds;
if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation))
bounds.size = CGSizeMake(bounds.size.height, bounds.size.width);
return bounds;
【讨论】:
很糟糕,但在可以投入时间正确修复问题之前完成工作:-)【参考方案11】:以下方法可用于查找给定方向的屏幕边界,与 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];
【讨论】:
【参考方案12】:这就是我用来计算正确矩形的:
UIScreen* const mainScreen = [UIScreen mainScreen];
CGRect rect = [mainScreen bounds];
#ifdef __IPHONE_8_0
if ([mainScreen respondsToSelector:@selector(coordinateSpace)])
if ([mainScreen respondsToSelector:@selector(fixedCoordinateSpace)])
id tmpCoordSpace = [mainScreen coordinateSpace];
id tmpFixedCoordSpace = [mainScreen fixedCoordinateSpace];
if ([tmpCoordSpace respondsToSelector:@selector(convertRect:toCoordinateSpace:)])
rect = [tmpCoordSpace convertRect:rect toCoordinateSpace: tmpFixedCoordSpace];
#endif
【讨论】:
【参考方案13】:只需添加上面回答的出色 cbartel 函数的 swift 版本。
func screenSize() -> CGSize
let screenSize = UIScreen.mainScreen().bounds.size
if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)
return CGSizeMake(screenSize.height, screenSize.width)
return screenSize
【讨论】:
【参考方案14】:我的问题与正在减号的 UIWindows 框架有关。 所以在 MyViewController 中编写了如下代码 -(NSUInteger)supportedInterfaceOrientations 方法
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.view setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];
[appDel.window setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];
它的工作对我来说试试吧。
【讨论】:
【参考方案15】:iOS 8 或更高版本
对于那些想要以点数计算屏幕尺寸(3.5 英寸屏幕有 320 × 480 点,4.0 英寸屏幕有 320 × 568 点等)的解决方案是
- (CGSize)screenSizeInPoints
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
if (width > height)
return CGSizeMake(height, width);
else
return [[UIScreen mainScreen] bounds].size;
【讨论】:
【参考方案16】:我注意到的一件事是,Info.plist 中支持的界面方向的顺序 确实很重要。我的应用程序遇到了这个问题的问题(在代码中执行方向),但我没有指定默认方向是纵向的任何地方。
我认为无论如何默认方向是纵向。
重新排列 Info.plist 中的元素,将肖像放在首位,恢复了预期的行为。
【讨论】:
【参考方案17】:这将在 iOS7 和 iOS8 中提供正确的设备,
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define IS_PORTRAIT UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)
+ (BOOL)isIPHONE4
// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0"))
if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0)
return YES;
else
return NO;
// >= iOS 8.0
else
if(IS_PORTRAIT)
if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0)
return YES;
else
return NO;
else
if ([self getDeviceWidth] == 480.0 && [self getDeviceHeight] == 320.0)
return YES;
else
return NO;
+ (BOOL)isIPHONE5
// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0"))
if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0)
return YES;
else
return NO;
// >= iOS 8.0
else
if(IS_PORTRAIT)
if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0)
return YES;
else
return NO;
else
if ([self getDeviceWidth] == 568.0 && [self getDeviceHeight] == 320.0)
return YES;
else
return NO;
+ (BOOL)isIPHONE6
// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0"))
if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0)
return YES;
else
return NO;
// >= iOS 8.0
else
if(IS_PORTRAIT)
if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0)
return YES;
else
return NO;
else
if ([self getDeviceWidth] == 667.0 && [self getDeviceHeight] == 375.0)
return YES;
else
return NO;
+ (BOOL)isIPHONE6Plus
// < iOS 8.0
if(SYSTEM_VERSION_LESS_THAN(@"8.0"))
if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0)
return YES;
else
return NO;
// >= iOS 8.0
else
if(IS_PORTRAIT)
if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0)
return YES;
else
return NO;
else
if ([self getDeviceWidth] == 736.0 && [self getDeviceHeight] == 414.0)
return YES;
else
return NO;
+ (CGFloat)getDeviceHeight
//NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
return [UIScreen mainScreen].bounds.size.height;
+ (CGFloat)getDeviceWidth
//NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
return [UIScreen mainScreen].bounds.size.width;
//您也可以添加更多设备(例如iPad)。
【讨论】:
问题不是关于基于屏幕分辨率的设备识别。此外,Apple 建议创建基于尺寸类而不是设备/屏幕检测的自适应 UI【参考方案18】:使用稍微修改过的 mnemia 的解决方案,即没有 iOS 版本检查的解决方案,在主屏幕边界上使用 min/max。
我需要一个CGRect
,所以从主屏幕边界得到CGRect
,并更改了size.width=min(w,h)
、size.height=max(w,h)
。然后我在代码中的两个地方调用了与操作系统无关的 get CGRect
函数,在那里我获得了OpenGL
的屏幕尺寸、触摸等。在修复之前我遇到了两个问题 - 在 IOS 8.x 上以横向模式显示位置OpenGL
视图不正确:左下角为全屏的 1/4。第二次触摸返回无效值。这两个问题都已按照说明进行了修复。谢谢!
【讨论】:
以上是关于[UIScreen mainScreen].bounds.size 在 iOS8 中是不是变得依赖于方向?的主要内容,如果未能解决你的问题,请参考以下文章
横向模式的“UIScreen mainScreen] applicationFrame]”
iPad:[UIScreen mainScreen].bounds 返回错误的坐标
为啥 [UIScreen mainScreen].bounds] 没有返回全屏尺寸?
[UIScreen mainScreen].bounds 与 [UIApplcation sharedApplication].keyWindow.bounds?