iPhone @2x Graphics, scale, and iPad

The iPhone 4's retina display has 2x the pixel pitch of the previous iPhones, giving the screen 4x as many pixels. It looks fantastic, and with dimensions exactly twice the original screen it means that the phone can pixel double graphics in apps that haven't been updated yet, and it looks fine (at least until you get used to using high resolution apps, and then its hard to go back). Apple gave developers an easy way to adapt apps to work with either screen. For UIKit based apps, if a view loads a graphic 'myButton.png' for the original resolution, then just by adding a file named 'myButton@2x.png' to the project in Xcode means that on iPhone 4 the app will load the @2x file instead. Simple and effective.

Detecting iPhone 4 / Scale

For graphics that aren't loaded as a project resource, you might need to programmatically determine if you want to draw or load @2x resolution or normal resolution. To determine this, you could use something like:

+ (BOOL *) isIPhone4 {
  return ([MyDeviceClass platformCode] isEqualToString:@"iPhone3,1"]);
}

+ (NSString *) platformCode {
  size_t size;
  sysctlbyname("hw.machine", NULL, &size, NULL, 0);
  char *machine = malloc(size);
  sysctlbyname("hw.machine", machine, &size, NULL, 0);
  NSString *platform = [NSString stringWithCString:machine encoding:[NSString defaultCStringEncoding]];
  free(machine);
  return platform;
}

However this isn't going to work on the simulator, iPad, or future devices. Instead, use the scale property of UIImage or UIScreen. Older versions of iOS don't have this property, so you have to test that its available before use:

+(BOOL) screenIs2xResolution {
  return 2.0 == [MyDeviceClass mainScreenScale];
}

+(CGFloat) mainScreenScale {
  CGFloat scale = 1.0;
  UIScreen* screen = [UIScreen mainScreen];
  if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
    scale = [screen scale];
   }
  return scale;
}

Scale on the iPad

So now you can code conditionally for iPhone 4's retina display, but what about the iPad running iOS 3.2? iPad can run iPhone apps at normal 1x size, or pixel doubled 2x size. When running 2x pixel doubled on the iPad, iOS3.2 will automatically use real screen pixels if its drawing UIImages and hence draw them at full resolution, which is a handy compromise. This _doesn't_ work for iOS4 targetted apps built with the @2x file names :-( but of course we can assume this will come to the iPad in the fall with iOS4.

iOS3.2 on the iPad does have support for the scale property. So if you want higher resolution graphics loaded programmatically when your iPhone app is running on the iPad, the code above will work. There's a catch though. When running an iPhone app in 1x mode on the iPad, scale returns 1.0, and when running in 2x mode scale returns 2.0. The first time someone uses your iPhone app on their iPad they might switch between 1x and 2x few times to decide which they prefer. So you could end up loading a mix of normal and 2x resolution images. If you are loading images from the net and caching them (very common in the apps we build), then you might end up drawing the low resolution image from cache even in @2x mode. In that case, a specific test for the iPad might be in order too when deciding which resolution to download and cache, so you always have the 2x files ready for the iPad:

+(BOOL) isIPad {
  BOOL isIPad=NO;
  NSString* model = [UIDevice currentDevice].model;
  if ([model rangeOfString:@"iPad"].location != NSNotFound) {
    return YES;
  }
  return isIPad;
}