How to detect iPhone 5 (widescreen devices)?

2018-12-31 03:09发布

I've just upgraded to XCode 4.5 GM and found out that you can now apply the '4" Retina' size to your view controller in the storyboard.

Now if I want to create an application that runs on both iPhone 4 and 5, of course I have to build every window twice, but I also have to detect whether the user has an iPhone with 3.5" or 4" screen and then apply the view.

How should I do that?

24条回答
公子世无双
2楼-- · 2018-12-31 03:46

this is the macro for my cocos2d project. should be the same for other apps.

#define WIDTH_IPAD 1024
#define WIDTH_IPHONE_5 568
#define WIDTH_IPHONE_4 480
#define HEIGHT_IPAD 768
#define HEIGHT_IPHONE 320

#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

//width is height!
#define IS_IPHONE_5 ( [ [ UIScreen mainScreen ] bounds ].size.height == WIDTH_IPHONE_5 )
#define IS_IPHONE_4 ( [ [ UIScreen mainScreen ] bounds ].size.height == WIDTH_IPHONE_4 )

#define cp_ph4(__X__, __Y__) ccp(cx_ph4(__X__), cy_ph4(__Y__))
#define cx_ph4(__X__) (IS_IPAD ? (__X__ * WIDTH_IPAD / WIDTH_IPHONE_4) : (IS_IPHONE_5 ? (__X__ * WIDTH_IPHONE_5 / WIDTH_IPHONE_4) : (__X__)))
#define cy_ph4(__Y__) (IS_IPAD ? (__Y__ * HEIGHT_IPAD / HEIGHT_IPHONE) : (__Y__))

#define cp_pad(__X__, __Y__) ccp(cx_pad(__X__), cy_pad(__Y__))
#define cx_pad(__X__) (IS_IPAD ? (__X__) : (IS_IPHONE_5 ? (__X__ * WIDTH_IPHONE_5 / WIDTH_IPAD) : (__X__ * WIDTH_IPHONE_4 / WIDTH_IPAD)))
#define cy_pad(__Y__) (IS_IPAD ? (__Y__) : (__Y__ * HEIGHT_IPHONE / HEIGHT_IPAD))
查看更多
姐姐魅力值爆表
3楼-- · 2018-12-31 03:46

In Swift, iOS 8+ project I like to make an extension on UIScreen, like:

extension UIScreen {

    var isPhone4: Bool {
        return self.nativeBounds.size.height == 960;
    }

    var isPhone5: Bool {
        return self.nativeBounds.size.height == 1136;
    }

    var isPhone6: Bool {
        return self.nativeBounds.size.height == 1334;
    }

    var isPhone6Plus: Bool {
        return self.nativeBounds.size.height == 2208;
    }

}

(NOTE: nativeBounds is in pixels).

And then the code will be like:

if UIScreen.mainScreen().isPhone4 {
    // do smth on the smallest screen
}

So the code makes it clear that this is a check for the main screen, not for the device model.

查看更多
时光乱了年华
4楼-- · 2018-12-31 03:46

I found that answers do not include a special case for Simulators.

#define IS_WIDESCREEN ( [ [ UIScreen mainScreen ] bounds ].size.height == 568  )
#define IS_IPHONE ([[ [ UIDevice currentDevice ] model ] rangeOfString:@"iPhone"].location != NSNotFound)
#define IS_IPAD ([[ [ UIDevice currentDevice ] model ] rangeOfString:@"iPad"].location != NSNotFound)
#define IS_IPHONE_5 ( IS_IPHONE && IS_WIDESCREEN )
查看更多
ら面具成の殇う
5楼-- · 2018-12-31 03:49

use the following Code:

CGFloat screenScale = [[UIScreen mainScreen] scale];

CGRect screenBounds = [[UIScreen mainScreen] bounds]; 

CGSize screenSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale); 

if (screenSize.height==1136.000000)
{ 
    // Here iPhone 5 View

    // Eg: Nextview~iPhone5.Xib
} else {
   // Previous Phones 

   // Eg : Nextview.xib
}
查看更多
梦醉为红颜
6楼-- · 2018-12-31 03:50

Tested and designed for any combination of SDK and OS:

Swift

Added iPad types. iPad 2 and iPad mini are non-retina iPads. While iPad Mini 2 & above, iPad 3, 4, iPad Air, Air 2, Air 3, and iPad Pro 9.7 have same logical resolution of 1024. iPad Pro has maxLength of 1366. Reference

import UIKit

public enum DisplayType {
    case unknown
    case iphone4
    case iphone5
    case iphone6
    case iphone6plus
    case iPadNonRetina
    case iPad
    case iPadProBig
    static let iphone7 = iphone6
    static let iphone7plus = iphone6plus
}

public final class Display {
    class var width:CGFloat { return UIScreen.main.bounds.size.width }
    class var height:CGFloat { return UIScreen.main.bounds.size.height }
    class var maxLength:CGFloat { return max(width, height) }
    class var minLength:CGFloat { return min(width, height) }
    class var zoomed:Bool { return UIScreen.main.nativeScale >= UIScreen.main.scale }
    class var retina:Bool { return UIScreen.main.scale >= 2.0 }
    class var phone:Bool { return UIDevice.current.userInterfaceIdiom == .phone }
    class var pad:Bool { return UIDevice.current.userInterfaceIdiom == .pad }
    class var carplay:Bool { return UIDevice.current.userInterfaceIdiom == .carPlay }
    class var tv:Bool { return UIDevice.current.userInterfaceIdiom == .tv }
    class var typeIsLike:DisplayType {
        if phone && maxLength < 568 {
            return .iphone4
        }
        else if phone && maxLength == 568 {
                return .iphone5
        }
        else if phone && maxLength == 667 {
            return .iphone6
        }
        else if phone && maxLength == 736 {
            return .iphone6plus
        }
        else if pad && !retina {
            return .iPadNonRetina
        }
        else if pad && retina && maxLength == 1024 {
            return .iPad
        }
        else if pad && maxLength == 1366 {
            return .iPadProBig
        }
        return .unknown
    }
}

See it in action https://gist.github.com/hfossli/bc93d924649de881ee2882457f14e346

Note: If e.g. iPhone 6 is in zoomed mode the UI is a zoomed up version of iPhone 5. These functions is not determining device type, but display mode thus iPhone 5 is the desired result in this example.

Objective-C

#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_RETINA ([[UIScreen mainScreen] scale] >= 2.0)

#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
#define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
#define SCREEN_MIN_LENGTH (MIN(SCREEN_WIDTH, SCREEN_HEIGHT))
#define IS_ZOOMED (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0)

#define IS_IPHONE_4_OR_LESS (IS_IPHONE && SCREEN_MAX_LENGTH < 568.0)
#define IS_IPHONE_5 (IS_IPHONE && SCREEN_MAX_LENGTH == 568.0)
#define IS_IPHONE_6 (IS_IPHONE && SCREEN_MAX_LENGTH == 667.0)
#define IS_IPHONE_6P (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0)

Usage: http://pastie.org/9687735

Note: If e.g. iPhone 6 is in zoomed mode the UI is a zoomed up version of iPhone 5. These functions is not determining device type, but display mode thus iPhone 5 is the desired result in this example.

查看更多
弹指情弦暗扣
7楼-- · 2018-12-31 03:52

First of all, you shouldn't rebuild all your views to fit a new screen, nor use different views for different screen sizes.

Use the auto-resizing capabilities of iOS, so your views can adjust, and adapt any screen size.

That's not very hard, read some documentation about that. It will save you a lot of time.

iOS 6 also offers new features about this, but this is still under NDA at the moment.
Be sure to read the API changelog on Apple Developer website, if you can access to it.

Edit: As iOS 6 is now out, check the new AutoLayout capabilities.

That said, if you really need to detect the iPhone 5, you can simply rely on the screen size.

[ [ UIScreen mainScreen ] bounds ].size.height

The iPhone 5's screen has a height of 568.
You can imagine a macro, to simplify all of this:

#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )

The use of fabs with the epsilon is here to prevent precision errors, when comparing floating points, as pointed in the comments by H2CO3.

So from now on you can use it in standard if/else statements:

if( IS_IPHONE_5 )
{}
else
{}

Edit - Better detection

As stated by some people, this does only detect a widescreen, not an actual iPhone 5.

Next versions of the iPod touch will maybe also have such a screen, so we may use another set of macros.

Let's rename the original macro IS_WIDESCREEN:

#define IS_WIDESCREEN ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )

And let's add model detection macros:

#define IS_IPHONE ( [ [ [ UIDevice currentDevice ] model ] isEqualToString: @"iPhone" ] )
#define IS_IPOD   ( [ [ [ UIDevice currentDevice ] model ] isEqualToString: @"iPod touch" ] )

This way, we can ensure we have an iPhone model AND a widescreen, and we can redefine the IS_IPHONE_5 macro:

#define IS_IPHONE_5 ( IS_IPHONE && IS_WIDESCREEN )

Also note that, as stated by @LearnCocos2D, this macros won't work if the application is not optimised for the iPhone 5 screen (missing the Default-568h@2x.png image), as the screen size will still be 320x480 in such a case.

I don't think this may be an issue, as I don't see why we would want to detect an iPhone 5 in a non-optimized app.

IMPORTANT - iOS 8 support

On iOS 8, the bounds property of the UIScreen class now reflects the device orientation.
So obviously, the previous code won't work out of the box.

In order to fix this, you can simply use the new nativeBounds property, instead of bounds, as it won't change with the orientation, and as it's based on a portrait-up mode.
Note that dimensions of nativeBounds is measured in pixels, so for an iPhone 5 the height will be 1136 instead of 568.

If you're also targeting iOS 7 or lower, be sure to use feature detection, as calling nativeBounds prior to iOS 8 will crash your app:

if( [ [ UIScreen mainScreen ] respondsToSelector: @selector( nativeBounds ) ] )
{
    /* Detect using nativeBounds - iOS 8 and greater */
}
else
{
    /* Detect using bounds - iOS 7 and lower */
}

You can adapt the previous macros the following way:

#define IS_WIDESCREEN_IOS7 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#define IS_WIDESCREEN_IOS8 ( fabs( ( double )[ [ UIScreen mainScreen ] nativeBounds ].size.height - ( double )1136 ) < DBL_EPSILON )
#define IS_WIDESCREEN      ( ( [ [ UIScreen mainScreen ] respondsToSelector: @selector( nativeBounds ) ] ) ? IS_WIDESCREEN_IOS8 : IS_WIDESCREEN_IOS7 )

And obviously, if you need to detect an iPhone 6 or 6 Plus, use the corresponding screen sizes.

Final note

Comments and suggestions have been incorporated in this post.
Thanks to everybody.

查看更多
登录 后发表回答