基础知识
屏幕尺寸
手机屏幕对角线的长度 1英寸(inch)=2.54厘米(cm)分辨率
在我们手机上呈现的一条线,一个面,一张图像都是由最小的单位像素来表示的,也可以简单理解为是由一个个小方块组成的。
例:当5.2英寸的手机分辨率1920px*1080px
即手机在竖向的高度上有1920个像素块,在横向的宽度上有1080个像。-
像素密度
屏幕像素密度,即每英寸屏幕所拥有的像素数,英文简称PPI
每英寸不是每平方英寸的简称,
这个英寸跟之前手机屏幕的尺寸一样,也是对角线的长度
即在一个对角线长度为1英寸的正方形内所拥有的像素数。
例: 5.2寸手机分辨率1920 *1080 px 密度约为424
(1920px就是公式里的纵向,1080px就是公式里的横向)
-
像素没有固定的物理性大小
苹果6手机 ,5英寸, 1920 * 1080 px, 469 ppi
荣耀7手机, 5.2英寸,1920 *1080px, 424 ppi
分辨率相同,苹果手机的屏幕尺寸比华为荣耀7小了0.2英寸,
但苹果(PPI)却比华为荣耀7高了45个PPI。一个像素其实就是一个色彩块,
同样的一英寸,苹果手机展示469个色彩,华为展示424个色彩,
苹果手机的显示效果当然就更好了。 -
电脑调整分辨率,是弄啥呢
在同一个设备上,它的像素个数是固定的,厂商在出厂时就设置好了。只有不同的设备之间,才有像素大小的区别。既然在同一个设备上,像素点数早就设定好了,那电脑上可以调整分辨率是怎么回事?系统给你推荐的是1366px*768px的分辨率,
意味着微软在这块屏幕上横向设置了768个像素,竖向设置了1366个像素。这个数字是确定且不会改变的。如果把分辨率调成800 * 600,系统就会分配给你800 * 600个有效像素个数,也就是真实的色彩块。其他的个数呢,就由系统自作主张,通过一系列运算给你一个模拟色彩块,填充成正好1366*768个色彩块。
导航栏高度
iphone 系列开发尺寸
手机型号 | Physical Device | Points (pt) | Rendered Pixels(px) | Render | 屏幕密度 |
---|---|---|---|---|---|
2G 3G 3GS | 3.5 | 320*480 | 320*480 | @2x | 326 |
4 4S | 3.5 | 320*480 | 640*960 | @2x | 326 |
5 5s 5c SE | 4 | 320*568 | 640*1136 | @2x | 326 |
6 6s 7 8 | 4.7 | 375*667 | 750*1334 | @2x | 326 |
6+ 6S+ 7+ 8+ | 5.5 | 414*736 | 1242*2208 | @3x | 401 |
X Xs | 5.8 | 375*812 | 1125*2436 | @3x | |
Xr | 6.1 | 414*896 | 828*1792 | @2x | |
Xs Max | 6.5 | 414*896 | 1242*2688 | @3x |
判断iPhoneX Type机型的方法汇总
方式一:通过获取设备的 device model 来判断
每一台 iOS 设备都有对应的硬件编码/标识符,
称为 device model 或者叫 machine name,
可以通过如下两种方法来获取 device model/machine name
#import
#import
// 获取 device model/machine name 的方法一
+ (NSString *)machineName1 {
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = (char *)malloc(size);
if (machine == NULL) {
return nil;
}
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
free(machine);
return platform;
}
// 获取 device model/machine name 的方法二
+ (NSString *)machineName2 {
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
}
iPhone X 对应的 device mode 为 iPhone10,3 和 iPhone10,6
iPhone XS 对应 iPhone11,2,
iPhone XS Max 对应 iPhone11,4 和 iPhone11,6,
iPhone XR 对应 iPhone11,8
参考链接
上述两种获取 device model 的方法在模拟器中运行得到的值为 i386 或 x86_64,
因此在模拟器中正确获取模拟器所对应的 device model:
// 获取模拟器所对应的 device model
NSString *model = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
综上完整代码应该是
+ (BOOL)isiPhoneX {
static BOOL isiPhoneX = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_IPHONE_SIMULATOR
// 获取模拟器所对应的 device model
NSString *model = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
#else
// 获取真机设备的 device model
struct utsname systemInfo;
uname(&systemInfo);
NSString *model = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
#endif
// 判断 device model 是否为 "iPhone10,3" 和 "iPhone10,6" 或者以 "iPhone11," 开头
// 如果是,就认为是 iPhone X
isiPhoneX = [model isEqualToString:@"iPhone10,3"] || [model isEqualToString:@"iPhone10,6"] || [model hasPrefix:@"iPhone11,"];
});
return isiPhoneX;
}
方式二:通过获取屏幕的宽高来判断
目前 iPhone X 设备的屏幕宽高对应的开发尺寸只有两种,
分别为 375pt * 812pt 和 414pt * 896pt,因此我们可以根据屏幕的高度来判断设备是否为 iPhone X。
但是此时需要考虑设备处于横屏或者竖屏的情况
在 UIDevice 中提供了一个 orientation 属性用于获取设备的方向(横向、竖向、或者水平),一开始我们想着先通过这个属性判断设备处于横屏或者竖屏,然后分别取其对应的屏幕宽度(横屏下)或者高度(竖屏下)来判断,但是当这个属性的值为 FaceUp 或者 FaceDown(即设备放在水平面上),我们是无法知道此时设备是处于横屏还是竖屏的。
后面我们想了一个简便的方法,即获取屏幕的宽度和高度,取较大一方进行比较是等于 812.0 或 896.0,代码如下:
+ (BOOL)isiPhoneX {
// 先判断当前设备是否为 iPhone 或 iPod touch
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
// 获取屏幕的宽度和高度,取较大一方判断是否为 812.0 或 896.0
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat maxLength = screenWidth > screenHeight ? screenWidth : screenHeight;
if (maxLength == 812.0f || maxLength == 896.0f) {
return YES;
}
}
return NO;
}
方式三:通过底部安全区域的高度来判断
iPhone X 发布后,为了适配顶部的浏览和底部的操作条,苹果在 iOS 11 上引入安全区域概念,建议开发者在安全区域内进行 UI 布局,因此我们可以获取屏幕 keyWindow 的 safeAreaInsets 值来判断设备是否 iPhone X。
//iPhone X
//竖屏,keyWindow 的 safeAreaInsets 值为:
{top: 44, left: 0, bottom: 34, right: 0}
//横屏
{top: 0, left: 44, bottom: 21, right: 44}
我们可以比较 safeAreaInsets 的 bottom 是否等于 34.0 或者 21.0 来判断设备是否为 iPhone X,
因为其他设备对应的 bottom 横竖屏下都为 0
+ (BOOL)isiPhoneX {
if (@available(iOS 11.0, *)) {
UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
// 获取底部安全区域高度,iPhone X 竖屏下为 34.0,横屏下为 21.0,其他类型设备都为 0
CGFloat bottomSafeInset = keyWindow.safeAreaInsets.bottom;
if (bottomSafeInset == 34.0f || bottomSafeInset == 21.0f) {
return YES;
}
}
return NO;
}
不足:必须在 AppDelegate 的 didFinishLaunchingWithOptions 回调中等 keyWindow 初始化之后才能正确判断
方式四:通过是否支持 FaceID 判断
由于目前只有 iPhone X 设备支持 FaceID,因此我们也可以通过判断设备是否支持 FaceID 来判断,代码如下:
+ (BOOL)canUseFaceID {
if (@available(iOS 11.0, *)) {
// will fail if user denies `canEvaluatePolicy:error:`
LAContext *context = [[LAContext alloc] init];
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
return (context.biometryType == LABiometryTypeFaceID);
}
}
return NO;
}
不足:如果用户禁用 canEvaluatePolicy:error: 方法的使用将无法正确判断,而且在也不适用于模拟器中的判断。
方式五:通过 UIStatusBar 的高度判断
在 iPhone X 之前,所有 iPhone 设备的 StatusBar(状态栏)高度都为 20pt,而 iPhone X 的为 44pt,因此我们可以通过获取状态栏的高度判断是否等于 44.0 来检测设备是否为 iPhone X,代码如下:
+ (BOOL)isiPhoneX {
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
if (statusBarFrame.size.height == 44.0f) {
return YES;
}
return NO;
}
不足:该方法只适用于竖屏且显示状态栏的情况下才能正确检测,
而在横屏模式下,或者 App 隐藏导航栏时,获取到的状态栏高度都为 0(statusBarFrame 的值为 CGRectZero),就无法判断了。
系统版本的宏
#define IOS [[[UIDevice currentDevice] systemVersion] floatValue]
#define IOS8 [[[UIDevice currentDevice] systemVersion] floatValue] >= 8 ? YES : NO
#define IOS9 [[[UIDevice currentDevice] systemVersion] floatValue] >= 9 ? YES : NO
#define IOS10 [[[UIDevice currentDevice] systemVersion] floatValue] >= 10 ? YES : NO
#define IOS11 [[[UIDevice currentDevice] systemVersion] floatValue] >= 11 ? YES
其它宏
// debug下打印日志, release下不执行NSLog代码
#ifdef DEBUG
#define NSLog(...) printf("myAppInfo %s\n %s\n",__func__, [[NSString stringWithFormat:__VA_ARGS__]UTF8String]);
#else
#define NSLog(format, ...)
#endif