Detect Retina Display
Solution 1
In order to detect the Retina display reliably on all iOS devices, you need to check if the device is running iOS4+ and if the [UIScreen mainScreen].scale
property is equal to 2.0. You CANNOT assume a device is running iOS4+ if the scale
property exists, as the iPad 3.2 also contains this property.
On an iPad running iOS3.2, scale will return 1.0 in 1x mode, and 2.0 in 2x mode -- even though we know that device does not contain a Retina display. Apple changed this behavior in iOS4.2 for the iPad: it returns 1.0 in both 1x and 2x modes. You can test this yourself in the simulator.
I test for the -displayLinkWithTarget:selector:
method on the main screen which exists in iOS4.x but not iOS3.2, and then check the screen's scale:
if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
([UIScreen mainScreen].scale == 2.0)) {
// Retina display
} else {
// non-Retina display
}
Solution 2
@sickp's answer is correct. Just to make things easier, add this line into your Shared.pch file:
#define IS_RETINA ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale >= 2.0))
Then in any file you can just do:
if(IS_RETINA)
{
// etc..
}
Solution 3
+(BOOL)iPhoneRetina{
return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?1:0;
}
Solution 4
Here is a handy swift extension:
Update for 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
}
}
Usage:
if UIScreen.main.isRetina {
// Your code
}
Original:
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
}
}
Usage:
if UIScreen.mainScreen().isRetina() {
// your code
}
Solution 5
It always feels a bit dodgy to compare floating-point values for equality. I prefer going for either
[UIScreen mainScreen].scale > 1.0;
or
[UIScreen mainScreen].scale < 2.0;
Related videos on Youtube
Pierre Valade
I'm excited about making the world a better place using new technologies. You can follow me on Twitter and read my blog.
Updated on March 13, 2020Comments
-
Pierre Valade about 4 years
Does iOS SDK provides an easy way to check if the currentDevice has an high-resolution display (retina) ?
The best way I've found to do it now is :
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) { // RETINA DISPLAY }
-
Michael Behan over 13 yearsOut of curiosity - what are you doing when you detect the display other than showing larger versions of your art work?
-
SinisterMJ over 13 yearspossible duplicate of How to differentiate between iphone4 and iphone 3
-
Pierre Valade over 13 years@mbehan: I have a TTImageView (see Three20 framework) and I want to give an high-resolution url of the image.
-
Pedro over 11 yearsThis question is also useful to me because I have downloaded images that present as UI available in sizes for all 4 display sizes & only want to have users download the appropriate one.
-
user3099609 over 9 years@mbehan: in my case I wanted custom cell separators that are 1px on both retina & non-retina screens (like the native separators). Setting the thickness to 1px renders at 2px on retina displays (obviously).
-
James over 9 yearsDon't forget that with the iPhone6 and 6 plus you need to test for scale '>= 2.0', not just '== 2.0'
-
-
makdad about 12 yearsYou say that "Apple changed this behavior in iOS4.2 for the iPad", implying that in iOS4.1, your code above would return "is Retina" for an iPad running an iPhone app in 2x mode. Am I wrong?
-
Jonny about 12 yearsThere never was a 4.1 for iPad. Only 3.2, then 4.2.
-
n13 almost 12 yearsThis call is a bit expensive so I'd initialize a BOOL with it on app start and use that in the app.
-
d11wtq over 11 yearsWhy the
?1:0
? Isn't that just reiterating what has already been calculated in the boolean part of the expression? -
fishinear about 11 yearsComparing two floating-point values for equality "feels dodgy", because they can differ slightly from integral values after computations. But comparing with < or > is just as dodgy in those situations. In this case, however, there is no chance at all that scale is not exactly 1.0 or 2.0, as it is hardware defined.
-
Danyal Aytekin almost 11 yearsAs @fishinear suggests, better to use something like
isRetina = [UIScreen mainScreen].scale > 1.95
. This will also have the benefit of being resilient to when @4x comes along :) -
Ricardo Sanchez-Saez over 10 yearsI strongly disagree. Doing this when not needed makes code less readable. The point on future-proofness might have its validity, but I doubt we'll have @4x screens any time soon (if at all).
-
Dan Rosenstark over 10 yearsThanks for the caching code. My only suggestion is to make this
(Util)
instead of(RetinaCheck)
... perhaps it's less clear, but it lends itself to other uses. Also I would name the methodisRetinaDisplay
or something that starts withis
, but maybe I never did understand the guidelines for Obj-C. Also, I'm a fan of> 1.0
but who knows what will make sense moving forward. -
Dan Rosenstark over 10 yearsI do suspect that the caching of
alreadyChecked
is gratuitous, but it's fine. -
Sandy Chapman over 10 yearsI prefer to check the version using
[UIDevice currentDevice].systemVersion]
. In this case it'd beNSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
-
tempire about 10 yearsThis particular usage has been superseded by SAMCategories within SSToolKit: [[UIScreen mainScreen] sam_isRetina]
-
Fattie about 10 yearsWrong. just because it is "hardware defined" does not, in any way, mean you avoid the compare-a-float problem. (It's just a float like any other.) As with any float, in general you can never use ==, you must use a > or < comparison. What about >1.5 for certainty.
-
Nikolay Shubenkov about 10 yearsBest solution as I think.
-
Dan Rosenstark about 10 years@NikolayShubenkov that's why I set alreadyChecked last. In the worst case scenario, you run the code to check an extra time or two.
-
Nikolay Shubenkov about 10 yearsI mean when one process will try to alreadyChecked while other is currently reading this value app may crash. I would add that line: @synchronyze(alreadyChecked){alreadyChecked = YES}
-
Isaac Paul almost 10 yearsDoesn't seem to work in the simulator for non retina iPad (ios 7.1) in xcode 4... weird.
-
arniotaki about 9 yearsThis does not work on simulator. Is it because of the respondsToSelector? Simulator does not respond to selector?
-
Ivan Carosati about 9 yearsGreat! However if you want to take in account the iPhone 6 Plus you should be checking for scale >= 2.0.
-
Alexander Farber about 9 yearsWhy checking for
@selector(displayLinkWithTarget:selector:)
and not for@selector(scale)
? -
C0D3 about 4 yearsShouldn't the code that's update to work for Swift 5 isRetinaHD check if iscreenScale is >= 3.0 not 2.0 ??? Edit: I updated it...