19

Given a Cocoa application which runs on Mac OS X 10.7 and later:

What is the best way to check, at runtime, if your app is currently running on a Mac with at least one retina display attached?

If checking for this sort of thing is just really wrong-headed, I fully welcome a well-reasoned explanation of why (and I will up-vote such answers if they are good).

But I'd still like to know :).

It seems likely you could just do a check specifically for the new Mac Book Pro "Retina" hardware (the only Mac which currently has a retina display), but I'd really prefer a more general/generic/future-proof way to check than this.

Ideally, I'd like to know how to detect the retina display, not the specific Mac model which currently happens to ship with a retina display.

Todd Ditchendorf
  • 11,217
  • 14
  • 69
  • 123

3 Answers3

33

If you really do need to do this, take a look at -[NSScreen backingScaleFactor]. But, this does seem wrong-headed, at least without knowing more about why you want to know.

While there is currently only one Mac with a Retina display, there may eventually be standalone displays that support Retina (and can be attached/detached at runtime) and you may be able to configure the built-in Retina display in 1x mode. Thus the answer to the question "is there a Retina display attached" can change at any time.

Rather, you may want to know if your content should be drawn with a given scale by using the -convert*ToBacking: methods or -[NSWindow backingScaleFactor]. For a ton more detail, watch the WWDC 2012 session video "Advanced Tips and Tricks for High Resolution on OS X" (when posted, hopefully within the next few weeks).

tjw
  • 982
  • 5
  • 7
  • 4
    +1, also OP should read the [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Introduction/Introduction.html). – Ken Thomases Jun 16 '12 at 23:21
  • 3
    It's not wrongheaded to do it. Web-sites absolutely need to do it in order to provide the correct resources to iPads and iPhones right now, so why should it be different for apps? Of course, it's wrongheaded to do your own checking *instead of* letting the OS do it in the simple asset-swapping case (i.e., the @2x case). :) – God of Biscuits Jun 17 '12 at 01:50
  • 2
    A web browser may need to download 2x images, but only if the window for the browser is on a 2x display... which could change at any time due to the reasons noted above. – tjw Jun 19 '12 at 01:38
  • 1
    Not all of the UIKit/AppKit classes support @2x resource swapping in a manner your users might expect (NSImageView is a case in point). Don't be too guarded here as there are "right-headed" reasons to break all sorts of development "taboos". – ctpenrose Aug 13 '13 at 23:33
  • 1
    +1 but please don't even suggest people about `-[NSScreen backingScaleFactor]`, I see too often `[[NSScreen mainScreen] backingScaleFactor]` and this is **so** wrong! I wrote a post on that [NSView,NSCell,CALayer Draw for Retina Display](http://blog.mazaheri.me/post/65931558994/cocoa-please-never-draw-your-nsview-nscell-and) – Micha Mazaheri Nov 03 '13 at 21:45
  • Micha Mazaheri link is expired – Sangram Shivankar Aug 14 '18 at 09:08
  • 1
    See this http://supermegaultragroovy.com/2012/10/24/coding-for-high-resolution-on-os-x-read-this/ – KamyFC Sep 21 '18 at 10:50
9

I just ran into an instance where I needed to detect if any screen was high resolution, this worked

float displayScale = 1;
    if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {
        NSArray *screens = [NSScreen screens];
        NSUInteger screenCount = screens.count
        for (int i = 0; i < screenCount; i++) {
            float s = [screens[i] backingScaleFactor];
            if (s > displayScale)
                displayScale = s;
        }
    }
Bryan
  • 4,628
  • 3
  • 36
  • 62
nuclearnova
  • 785
  • 7
  • 10
  • 4
    You do know about fast iteration as in `for (NSScreen *screen in [NSScreen screens])` ? Saves you a lot of typing (and is faster at runtime). – DarkDust Nov 08 '12 at 10:57
  • yes,this one works! thumbs up! float displayScale = 1.0; if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {for (NSScreen *screen in [NSScreen screens]) {float s = [screen backingScaleFactor];if ( s > displayScale) displayScale = s;}}NSLog(@"displayScale=%f",displayScale);float x0 = displayScale * self.frame.origin.x;float y0 = displayScale * self.frame.origin.y;float w0 = displayScale * self.frame.size.width;float h0 = displayScale * self.frame.size.height;NSLog(@"x=%f;y=%f;w=%f'h=%f",x0,y0,w0,h0); – Jiulong Zhao Nov 22 '12 at 00:33
5

Well, on iOS you use the UIScreen.scale property. If it returns 2.0, then you're on a device with a high resolution display. Otherwise you're on a low-resolution device.

So I'd imagine that on Mac OS you could use either -[NSScreen backingScaleFactor] or -[NSWindow backingScaleFactor].

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 2
    backingScaleFactor is not a class method. So, it should be [[NSScreen mainScreen] backingScaleFactor] or one of the [NSScreen screens]'s. – erkanyildiz Nov 12 '12 at 18:58
  • Please forget about `-[NSScreen backingScaleFactor]`, the right way to do is adapt your drawing code depending on the window's scale factor. The system is responsible of determining which scale factor is appropriate for a given window. – Micha Mazaheri Nov 03 '13 at 21:48
  • 2
    @MichaMazaheri: Sometimes you don’t need this for drawing. In my case I’m taking a screenshot from multiple screens and then you have to calculate the accurate pixel-position. -[NSScreen backingScaleFactor] is the way to go. – Git.Coach Nov 21 '13 at 14:23
  • @ThomasJohannesmeyer I agree! But that's some rare cases, most of the apps only need to care about their windows, not screens. – Micha Mazaheri Nov 21 '13 at 22:06