23

In my application I am downloading some images from the web (from my server to be precise), in order to save some bandwith and especially memory on the phone, I provide them in two resolutions: 480x320 for the "old" iPhone series and in 960x640 for the iPhone 4 with the retina display. Now I need to be able to detect from within the app when it is running on a device that supports the retina screen. How could I do that?

I have been thinking about using the code snippet below which would return me a specific device identifier such as eg. "iPhone3", yet then I would be limiting the detection to the iPhone4 and would need to update that code for any subsequent device with a retina display.

size_t size;

// Set 'oldp' parameter to NULL to get the size of the data
// returned so we can allocate appropriate amount of space
sysctlbyname("hw.machine", NULL, &size, NULL, 0); 

// Allocate the space to store name
char *name = malloc(size);

// Get the platform name
sysctlbyname("hw.machine", name, &size, NULL, 0);

// Place name into a string
NSString *machine = [NSString stringWithCString:name];

Is there any better soution (maybe it is very obvious but I missed it)?

Robin
  • 8,197
  • 11
  • 45
  • 74

11 Answers11

32

Just did some reading on the official Apple Developers Forums and the issues has been discussed in length there. The best way to me seems to be the use of the scale property of UIScreen. Although it is only available in iOS 4 and later, it will tell you everything you need to know and will most likely play an even more important role in the future (already noticed that the iPad's screen resolution of 1024x768 is exactly 32/15 * 480x320?).

UIScreen.mainScreen.scale

If you have yet another idea feel free to post it :)

devios1
  • 36,899
  • 45
  • 162
  • 260
Robin
  • 8,197
  • 11
  • 45
  • 74
  • 2
    Yes, use this, don't ever try to query which device you're running on, it will end in nothing but pain. – Joshua Weinberg Jul 17 '10 at 23:25
  • That's the correct approach, as you don't want to write conditional code for the iPhone 4 but for a display with a different scale / resolution. Who knows when the iPod will get the retina treatment. – Felix Lamouroux Jul 18 '10 at 09:38
  • 1
    @Felix: hopefully in the next update, expected in January. And wouldn't it be just agonizingly nifty to have our code magically support it without any tweakage? Hence the [UIScreen scale] as the best approach. – Dan Ray Aug 26 '10 at 12:20
23

Here is some code to do it the right way for both iOS 3.x and 4.x:

BOOL hasHighResScreen = NO;
if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
    CGFloat scale = [[UIScreen mainScreen] scale];
    if (scale > 1.0) {
        hasHighResScreen = YES;
    }
}
Scott Gustafson
  • 342
  • 2
  • 3
  • Just beware that this will also return true for the iPad's "iphone simulator" mode if it starts at 2x, which may or may not be what you want. – Matt Rix Sep 29 '10 at 22:29
  • 2
    Is there a workaround for the 2x issue on the iPad's "iphone simulator"? – joelsand Jan 17 '11 at 15:33
13

A little update on answer by Scott Gustafson:

If we need to distinguish between iPad/Retina/iphone:

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) 
    {
        CGFloat scale = [[UIScreen mainScreen] scale];

           if (scale > 1.0) 
           {
                //iPad retina screen
           }  
           else
           {
                //iPad screen
           }
    } 
    else
    {
         if ([UIScreen instancesRespondToSelector:@selector(scale)]) 
         {
               CGFloat scale = [[UIScreen mainScreen] scale];

               if (scale > 1.0) 
               {
                    if([[ UIScreen mainScreen ] bounds ].size.height == 568)
                    {
                        //iphone 5
                    }
                    else
                    {
                        //iphone retina screen
                    }
               }
               else
               {
                    //iphone screen
               }
         }
    }
Guntis Treulands
  • 4,764
  • 2
  • 50
  • 72
5

I get real screen size (in pixels) by this way:

UIScreen *MainScreen = [UIScreen mainScreen];
UIScreenMode *ScreenMode = [MainScreen currentMode];
CGSize Size = [ScreenMode size]; // <--- Real screen size
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
3
- (BOOL)isRetina {

    BOOL isRetina = NO;

    if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
        CGFloat scale = [[UIScreen mainScreen] scale];
        if (scale > 1.0) {
            isRetina = YES;
        }
    }

    return isRetina;
}


- (CGSize)sizeInPixels {

    CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
    CGSize screenSize = CGSizeMake(appFrame.size.width, appFrame.size.height);

    return [self isRetina] ? CGSizeMake(screenSize.width * 2, screenSize.height * 2) : screenSize;
}
berec
  • 775
  • 10
  • 19
1

And for those who just want to copy/paste how to detect iphone/iphone_retina/ipad/ipad_retina, here's what I ended up doing after reading this thread. Heavily inspired by Guntis Treulands' contribution, who in turn expanded upon Scott Gustafsons answer.

- (NSString *) yesButWhichDeviceIsIt {    
    BOOL hasRetina = NO;
    if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
        CGFloat scale = [[UIScreen mainScreen] scale];
        if (scale > 1.0) {
            hasRetina = YES;
        }
    }
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        if (hasRetina) {
            return @"iPad retina";
        } else {
            return @"iPad";
        }
    } else {
        if (hasRetina) {
            return @"iPhone retina";
        } else {
            return @"iPhone";
        }        
    }
}
thomax
  • 9,213
  • 3
  • 49
  • 68
1

Go with Robin's answer. One other note: if you do need to check the device name, just use the methods on UIDevice.

[[UIDevice currentDevice] model];
[[UIDevice currentDevice] systemName];
[[UIDevice currentDevice] systemVersion];
Matt Rix
  • 844
  • 7
  • 10
1
UIScreen *MainScreen = [UIScreen mainScreen];
UIScreenMode *ScreenMode = [MainScreen currentMode];
CGSize Size = [ScreenMode size]; // <--- Real screen size
Taryn
  • 242,637
  • 56
  • 362
  • 405
anup
  • 21
  • 1
0
+(BOOL)Retina{
        return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?1:0; }
Mani
  • 1,841
  • 15
  • 29
  • 2
    Normal Cocoa naming practice would dictate to name the method with a lower-case initial letter. – Monolo Jul 15 '12 at 11:51
0

If you're using Cocos2d, try the following:

[[CCDirector sharedDirector] winSizeInPixels];

It will return a CGSize with properties width and height.

codeperson
  • 8,050
  • 5
  • 32
  • 51
-1

Although you've already selected an answer, there is a much easier way when specifically dealing with images so I'll mention it as well.

If you include two images in your bundle of the two sizes (320x480 and 640x960) and you append "@2x" at the end of the latter image's filename, [UIImage imageNamed:] will automagically pick the smaller image for older devices and the 2x for devices with a retina display, provided you leave off the image suffix. Ex.:

2 images named @"image.png" and @"image@2x.png", both included in the app bundle.

Then call:

[UIImage imageNamed:@"image"];

This is also how app icons and the loading screen work.

rich.e
  • 3,660
  • 4
  • 28
  • 44
  • -1: Did you not read the start of the post...? To quote, "In my application I am **downloading some images from the web**" – JRG-Developer May 17 '13 at 05:01