Given the newly announced iPhone 6 screen sizes:
iPhone 6: 1334h * 750w @2x (in points: 667h * 375w)
iPhone 6+: 1920 * 1080 @3x (in points: 640h * 360w)
All three devices have (pretty much) the same number of points per inch. So your images will automatically be the same physical size.
Use [[UIScreen mainScreen] bounds]
to get the total number of points on the screen. Divide by 163 to get the approximate size in inches if you really want it.
Notice that the 6+ does not return 1080p because it doesn't render to a 1080p buffer. It renders such that output is approximately 160 points per inch, using @3x assets.
No need to second guess.
E.g. if you write this code:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 163, 163)];
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
You'll get a view that is pretty much the same physical size — one inch square — on all iOS devices.
Apple has already done the hard work, so you don't have to.
I use the following code to determine what device is running (it's a little bit quick and dirty but it does the trick)
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ){
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
if( screenHeight < screenWidth ){
screenHeight = screenWidth;
}
if( screenHeight > 480 && screenHeight < 667 ){
NSLog(@"iPhone 5/5s");
} else if ( screenHeight > 480 && screenHeight < 736 ){
NSLog(@"iPhone 6");
} else if ( screenHeight > 480 ){
NSLog(@"iPhone 6 Plus");
} else {
NSLog(@"iPhone 4/4s");
}
}
(This only works when iPhone 6 / 6 Plus is enabled by adding the appropriate splash screens)
The first screen will be the device screen, Note that a launch images for the new phones have to be added before, otherwise the app is running in Zoomed Mode for older apps : Here is the code I used to check this out. Note: This only works with version iOS 8 and higher:
UIScreen *mainScreen = [UIScreen mainScreen];
NSLog(@"Screen bounds: %@, Screen resolution: %@, scale: %f, nativeScale: %f",
NSStringFromCGRect(mainScreen.bounds), mainScreen.coordinateSpace, mainScreen.scale, mainScreen.nativeScale);
Code for detecting iPhone 6 Plus:
#define IS_PAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_PHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
-(BOOL)iPhone6PlusDevice{
if (!IS_PHONE) return NO;
if ([UIScreen mainScreen].scale > 2.9) return YES; // Scale is only 3 when not in scaled mode for iPhone 6 Plus
return NO;
}
or
-(BOOL) iPhone6PlusUnZoomed{
if ([self iPhone6PlusDevice]){
if ([UIScreen mainScreen].bounds.size.height > 720.0) return YES; // Height is 736, but 667 when zoomed.
}
return NO;
}
Note: If you are checking for iPhone 6 Plus, to adjust the user interface then don´t rely on .nativeScale
, because the simulator and actual device give different results. Due to the comment below. Scale is a CGFloat and thus, code should not check equality, because some floats values may never be equal.
After adding Launch Screen you can utilise the new iPhone sizes, otherwise your app will still look scaled.
Size for iPhone 11 Pro Max with @3x scaling, coordinate space: 414 x 896 points and 1242 x 2688 pixels, 458 ppi, device physical size is 3.06 x 6.22 in or 77.8 x 158.0 mm. 6.5" Super Retina XDR display.
Size for iPhone 11 Pro with @3x scaling, coordinate space: 375 x 812 points and 1125 x 2436 pixels, 458 ppi, device physical size is 2.81 x 5.67 in or 71.4 x 144.0 mm. 5.8" Super Retina XDR display.
Size for iPhone 11 with @2x scaling, coordinate space: 414 x 896 points and 828 x 1792 pixels, 326 ppi, device physical size is 2.98 x 5.94 in or 75.7 x 150.9 mm. 6.1" Liquid Retina HD display.
Size for iPhone X
On the physical device, iPhone 6 Plus's main screen's bounds is 2208x1242 and nativeBounds is 1920x1080. There is hardware scaling involved to resize to the physical display.
On the simulator, the iPhone 6 Plus's main screen's bounds and nativeBounds are both 2208x1242.
In other words... Videos, OpenGL, and other things based on CALayers that deal with pixels will deal with the real 1920x1080 framebuffer on device (or 2208x1242 on sim). Things dealing with points in UIKit will be deal with the 2208x1242 (x3) bounds and get scaled as appropriate on device.
The simulator does not have access to the same hardware that is doing the scaling on device and there's no really much of a benefit to simulating it in software as they'd produce different results than the hardware. Thus it makes sense to set the nativeBounds of a simulated device's main screen to the bounds of the physical device's main screen.
iOS 8 added API to UIScreen (nativeScale and nativeBounds) to let a developer determine the resolution of the CADisplay corresponding to the UIScreen.
to me this works for me
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
UIStoryboard *storyBoard;
CGSize result = [[UIScreen mainScreen] bounds].size;
CGFloat scale = [UIScreen mainScreen].scale;
result = CGSizeMake(result.width * scale, result.height * scale);
if(result.height == 1136){
storyBoard = [UIStoryboard storyboardWithName:@"Main_iPhone_5" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
} else if(result.height == 1334){
storyBoard = [UIStoryboard storyboardWithName:@"Main_iPhone_6" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
} else if(result.height == 2208){
storyBoard = [UIStoryboard storyboardWithName:@"Main_iPhone_6_plus" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
} else if(result.height == 960){
storyBoard = [UIStoryboard storyboardWithName:@"Main_iPhone_4" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
}
} else {
UIStoryboard *storyBoard;
storyBoard = [UIStoryboard storyboardWithName:@"Main_iPad" bundle:nil];
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[self.window setRootViewController:initViewController];
}
I had to detect the iPhone 6 Plus in an app built with iOS 7. Because nativeScale isn't available on [UIScreen mainScreen] I tried to use [UIScreen mainScreen] scale] but this just returned 2.0. So I came up with this solution to detect the iPhone 6 Plus on iOS 7 (should also work on iOS 8):
-(BOOL)iPhone6Plus{
BOOL isiPhone6Plus = NO;
SEL selector = NSSelectorFromString(@"scale");
if ([[UIScreen mainScreen] respondsToSelector:selector]) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
[[[UIScreen mainScreen] class] instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIScreen mainScreen]];
[invocation invoke];
float returnValue;
[invocation getReturnValue:&returnValue];
if (returnValue == 3.0) {
isiPhone6Plus = YES;
}
NSLog(@"ScaleFactor %1.2f", returnValue);
}
return isiPhone6Plus;
}
The interesting part of this code is, that if I use NSInvocation the return value of the scale selector will be 3.0. Calling this method directly on iOS 7 returns 2.0.