屏幕适配

基础知识

  • 屏幕尺寸
    手机屏幕对角线的长度 1英寸(inch)=2.54厘米(cm)

  • 分辨率
    在我们手机上呈现的一条线,一个面,一张图像都是由最小的单位像素来表示的,也可以简单理解为是由一个个小方块组成的。
    例:当5.2英寸的手机分辨率1920px*1080px
    即手机在竖向的高度上有1920个像素块,在横向的宽度上有1080个像。

  • 像素密度
    屏幕像素密度,即每英寸屏幕所拥有的像素数,英文简称PPI
    每英寸不是每平方英寸的简称,
    这个英寸跟之前手机屏幕的尺寸一样,也是对角线的长度
    即在一个对角线长度为1英寸的正方形内所拥有的像素数。
    例: 5.2寸手机分辨率1920 *1080 px 密度约为424
    (1920px就是公式里的纵向,1080px就是公式里的横向)

    屏幕适配_第1张图片

  • 像素没有固定的物理性大小
    苹果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个色彩块。

导航栏高度

屏幕适配_第2张图片
屏幕适配_第3张图片
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

你可能感兴趣的:(屏幕适配)