检测视网膜显示


223

iOS SDK是否提供一种简便的方法来检查currentDevice是否具有高分辨率显示器(视网膜)?

我发现现在最好的方法是:

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) {
         // RETINA DISPLAY
    }

出于好奇-除了显示较大版本的艺术品以外,您在检测显示器时正在做什么?
Michael Behan 2010年


@mbehan:我有一个TTImageView(请参见Three20框架),我想提供图像的高分辨率URL。
皮埃尔·瓦拉德

1
这个问题对我也很有用,因为我已经下载了以UI形式显示的图像,该图像具有所有4种显示尺寸的尺寸,并且只希望用户下载适当的图像。
2012年

@mbehan:在我的情况下,我想要在视网膜和非视网膜屏幕上都使用1px的自定义单元格分隔符(例如本机分隔符)。将厚度设置为1px会在视网膜显示器上渲染2px(很明显)。
user3099609 2014年

Answers:


295

为了在所有iOS设备上可靠地检测Retina显示屏,您需要检查设备是否正在运行iOS4 +,以及该[UIScreen mainScreen].scale属性是否等于2.0。如果该scale属性存在,则不能假定设备正在运行iOS4 + ,因为iPad 3.2也包含此属性。

在运行iOS3.2的iPad上,scale将在1x模式下返回1.0,在2x模式下返回2.0-即使我们知道该设备不包含Retina显示屏。苹果在iPad的iOS4.2中更改了此行为:它在1x和2x模式下均返回1.0。您可以在模拟器中对此进行测试。

-displayLinkWithTarget:selector:在存在于iOS4.x但不存在于iOS3.2的主屏幕上测试该方法,然后检查屏幕的缩放比例:

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0)) {
  // Retina display
} else {
  // non-Retina display
}

您说“ Apple在iPad的iOS4.2中更改了此行为”,这意味着在iOS4.1中,对于以2x模式运行iPhone应用程序的iPad,上面的代码将返回“ is Retina”。我错了吗?
makdad 2012年

9
iPad从来没有4.1。只有3.2,然后是4.2。
Jonny 2012年

11
这个调用有点贵,所以我在应用程序启动时用它初始化了一个BOOL并在应用程序中使用它。
2012年

我更喜欢使用来检查版本[UIDevice currentDevice].systemVersion]。在这种情况下,应该是NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
桑迪·查普曼

在xcode 4中的非视网膜iPad(ios 7.1)的模拟器中似乎无法使用...很奇怪。
艾萨克·保罗

81

@sickp的答案是正确的。为了使事情变得简单,请将以下行添加到您的Shared.pch文件中:

#define IS_RETINA ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale >= 2.0))

然后,您可以在任何文件中执行以下操作:

if(IS_RETINA)
{
   // etc..
}

这在模拟器上不起作用。是因为responsToSelector吗?模拟器不响应选择器?
arniotaki

2
大!但是,如果要考虑使用iPhone 6 Plus,则应检查比例> = 2.0。
伊万·卡罗萨蒂

20
+(BOOL)iPhoneRetina{
    return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?1:0;
}

23
为什么?1:0呢?这不只是重申表达式的布尔部分中已经计算出的值吗?
d11wtq

9

这是一个便捷的扩展:

Swift v5更新:

extension UIScreen {

    public var isRetina: Bool {
        guard let scale = screenScale else {
            return false
        }
        return scale >= 2.0
    }

    public var isRetinaHD: Bool {
        guard let scale = screenScale else {
            return false
        }
        return scale >= 3.0
    }

    private var screenScale: CGFloat? {
        guard UIScreen.main.responds(to: #selector(getter: scale)) else {
            return nil
        }
        return UIScreen.main.scale
    }
}

用法:

if UIScreen.main.isRetina {
    // Your code
}

原版的:

extension UIScreen { 
public func isRetina() -> Bool {
    return screenScale() >= 2.0
}

public func isRetinaHD() -> Bool {
    return screenScale() >= 3.0
}

private func screenScale() -> CGFloat? {
    if UIScreen.mainScreen().respondsToSelector(Selector("scale")) {
        return UIScreen.mainScreen().scale
    }
    return nil
    }
}

用法:

if UIScreen.mainScreen().isRetina() {
 // your code
        }

对于Swift 5 isRetinaHD更新的代码不应该检查iscreenScale是否> = 3.0而不是2.0?编辑:我更新了它
C0D3

6

这个片段...

    int d = 0; // standard display
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0) {
    d = 1; // is retina display
}

if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    d += 2;
}

将返回... 0表示标准分辨率的iPhone / iPod touch,1表示视网膜的iPhone,2表示标准分辨率的iPad,3表示视网膜的iPad。



5

比较浮点值是否相等总是让人有些疑惑。我更喜欢去

[UIScreen mainScreen].scale > 1.0;

要么

[UIScreen mainScreen].scale < 2.0;

5
比较两个浮点值是否相等“感觉模糊”,因为它们在计算后可能与整数值略有不同。但是,在这种情况下,与<或>进行比较也很容易。但是,在这种情况下,完全没有机会将比例尺精确地确定为1.0或2.0,因为它是由硬件定义的。
fishinear

正如@fishinear所建议的那样,最好使用isRetina = [UIScreen mainScreen].scale > 1.95。这也将具有抵御@ 4x出现的时间的好处:)
Danyal Aytekin 2013年

我非常不同意。在不需要时执行此操作会使代码的可读性降低。关于面向未来的观点也许有其正确性,但我怀疑我们很快就会有@ 4x屏幕(如果有的话)。
里卡多·桑切斯·塞兹

错误。仅仅因为它是“硬件定义的”并不以任何方式意味着您避免了比较浮点数问题。(这和其他浮点数一样都是浮点数。)与任何浮点数一样,通常不能使用==,而必须使用>或<比较。确定性大于1.5怎么办。
Fattie

2

这是对马特·MC的上述回答。只是一个类别UIScreen

#import "UIScreen+Util.h"

@implementation UIScreen (Util)

+ (BOOL) isRetinaDisplay {
    static BOOL retina = NO;
    static BOOL alreadyChecked = NO;
    if (!alreadyChecked) {
        UIScreen *mainScreen = self.mainScreen;
        if (mainScreen) {
            retina = mainScreen.scale > 1.0;
            alreadyChecked = YES;
        }
    }
    return retina;
}

@end

1
我确实怀疑缓存alreadyChecked是免费的,但这很好。
Dan Rosenstark

@NikolayShubenkov,这就是为什么我设置已经最后检查过的原因。在最坏的情况下,您运行代码来检查一两个额外的时间。
Dan Rosenstark 2014年

我的意思是,当一个进程将尝试已经被Checked而另一个进程正在读取此值时,该应用可能崩溃。我要添加以下行:@synchronyze(alreadyChecked){alreadyChecked = YES}
Nikolay Shubenkov 2014年

2

以上答案的Swift版本,缩放比例大于等于2.0,因此它包括iPhone 6+和其他比Retina缩放比例更高的未来设备:

 if UIScreen.mainScreen().respondsToSelector(Selector("scale")) && UIScreen.mainScreen().scale >= 2.0 {
    // code executed only on Retina device
}

1

只是为了结合@sickp的答案和@ n13的以下注释,我将其设置为似乎很好用的UIScreen类别。首次调用时会进行检查,然后保存以备后用。

@interface UIScreen (RetinaCheck)
+ (BOOL)retinaScreen;
@end

static BOOL isRetinaScreen = NO;
static BOOL didRetinaCheck = NO;

@implementation UIScreen (RetinaCheck)
+ (BOOL)retinaScreen
{
    if (!didRetinaCheck) {
        isRetinaScreen = ([[self mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
                          ([self mainScreen].scale == 2.0));
        didRetinaCheck = YES;
    }
    return isRetinaScreen;
}
@end

对某人可能有用。


感谢您的缓存代码。我唯一的建议是对此进行(Util)替代,而不是(RetinaCheck)...可能不太清楚,但它可用于其他用途。另外,我想说一下方法isRetinaDisplay或以开头的东西is,但是也许我从来没有理解过Obj-C的指南。另外,我还是的粉丝,> 1.0但谁知道前进的意义。
Dan Rosenstark

1
// .h
UIKIT_EXTERN bool isRetinaDisplay();

// .m
bool isRetinaDisplay()
{
    static bool flag;
#ifdef __BLOCKS__
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        {
            flag = [[UIScreen mainScreen] scale] > 1.0;
        }
        else
        {
            flag = false;
        }
    });
#else
    static bool onceToken;
    if(onceToken == false)
    {
        onceToken = true;
        if([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        {
            flag = [[UIScreen mainScreen] scale] > 1.0;
        }
        else
        {
            flag = false;
        }
    }
#endif
    return flag;
}

我认为最好的解决方案。
Nikolay Shubenkov 2014年

0

试试这个

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0))
{
    // Retina display
    NSLog(@"---------------Retina display");
} else {
    // non-Retina display
    NSLog(@"---------------non-Retina display");
}

0

primulaveris的修改版本,用于简化大多数常见用例。我使用的是Swift 2.2,但这没关系。

extension UIScreen {
    static var isRetina: Bool {
        return screenScale >= 2.0
    }

    static var isRetinaHD: Bool {
        return screenScale >= 3.0
    }

    static var screenScale:CGFloat {
        return UIScreen.mainScreen().scale
    }
}

然后像这样简单地使用它们

print(UIScreen.isRetina)
print(UIScreen.isRetinaHD)
print(UIScreen.screenScale)

0

这对我有用

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.