Detect Retina Display

99,176

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;
Share:
99,176

Related videos on Youtube

Pierre Valade
Author by

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, 2020

Comments

  • Pierre Valade
    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
      Michael Behan over 13 years
      Out of curiosity - what are you doing when you detect the display other than showing larger versions of your art work?
    • SinisterMJ
      SinisterMJ over 13 years
    • Pierre Valade
      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
      Pedro over 11 years
      This 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
      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
      James over 9 years
      Don't forget that with the iPhone6 and 6 plus you need to test for scale '>= 2.0', not just '== 2.0'
  • makdad
    makdad about 12 years
    You 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
    Jonny about 12 years
    There never was a 4.1 for iPad. Only 3.2, then 4.2.
  • n13
    n13 almost 12 years
    This call is a bit expensive so I'd initialize a BOOL with it on app start and use that in the app.
  • d11wtq
    d11wtq over 11 years
    Why the ?1:0 ? Isn't that just reiterating what has already been calculated in the boolean part of the expression?
  • fishinear
    fishinear about 11 years
    Comparing 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
    Danyal Aytekin almost 11 years
    As @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
    Ricardo Sanchez-Saez over 10 years
    I 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
    Dan Rosenstark over 10 years
    Thanks 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 method isRetinaDisplay or something that starts with is, 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
    Dan Rosenstark over 10 years
    I do suspect that the caching of alreadyChecked is gratuitous, but it's fine.
  • Sandy Chapman
    Sandy Chapman over 10 years
    I prefer to check the version using [UIDevice currentDevice].systemVersion]. In this case it'd be NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
  • tempire
    tempire about 10 years
    This particular usage has been superseded by SAMCategories within SSToolKit: [[UIScreen mainScreen] sam_isRetina]
  • Fattie
    Fattie about 10 years
    Wrong. 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
    Nikolay Shubenkov about 10 years
    Best solution as I think.
  • Dan Rosenstark
    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
    Nikolay Shubenkov about 10 years
    I 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
    Isaac Paul almost 10 years
    Doesn't seem to work in the simulator for non retina iPad (ios 7.1) in xcode 4... weird.
  • arniotaki
    arniotaki about 9 years
    This does not work on simulator. Is it because of the respondsToSelector? Simulator does not respond to selector?
  • Ivan Carosati
    Ivan Carosati about 9 years
    Great! However if you want to take in account the iPhone 6 Plus you should be checking for scale >= 2.0.
  • Alexander Farber
    Alexander Farber about 9 years
    Why checking for @selector(displayLinkWithTarget:selector:) and not for @selector(scale)?
  • C0D3
    C0D3 about 4 years
    Shouldn'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...