前言
*近写毕业设计的时候,发现 iPhoneX 之后的刘海屏手机顶部状态栏高度和底部TabBar高度和原来不一样了,这就需要我们对刘海屏手机做单独的 UI 布局适配了。

刘海屏判断
适配的核心是要对刘海屏进行判断,以下针对刘海屏手机的特征,提供了两种判断方法。

1. 安全区底部边距判断法
在 iOS 11 之后,多了安全区(下图蓝色区域)的概念。刘海屏手机因为多了下方的小黑条,底部安全区存在距离屏幕底部的边距,而且这是非刘海屏所不具有的。

%title插图%num

因此,我们可以将其作为判断刘海屏的依据。如果系统大于 iOS 11 且安全区底部到屏幕底部存在间距,就将其判断为刘海屏。代码如下:

// 刘海屏判断
#define iPhoneX ({ \
BOOL iPhoneX = NO; \
if (@available(iOS 11.0, *)) { \
if ([UIApplication sharedApplication].windows[0].safeAreaInsets.bottom > 0) { \
iPhoneX = YES; \
} \
} \
iPhoneX; \
})

2. 屏幕宽高比判断法
考虑到我不太喜欢写这种多行宏定义,据说会降低编译速度。我又继续寻找其他刘海屏的特征,终于在下面这一张图中找到了灵感。

%title插图%num

由上图所示,但凡是刘海屏手机,屏幕的纵横比都是 19 : 9。

设手机屏幕宽度为W i d t h WidthWidth,高度为 H e i g h t HeightHeight。根据屏幕纵横比规律,它只要满足以下两个条件其中一个,我们就能判它是刘海屏。
∣ H e i g h t W i d t h − 812 375 ∣ < 0.01 |\frac{Height}{Width} – \frac{812}{375}| < 0.01

Width
Height


375
812

∣<0.01

∣ H e i g h t W i d t h − 896 414 ∣ < 0.01 |\frac{Height}{Width} – \frac{896}{414}| < 0.01

Width
Height


414
896

∣<0.01

代码如下:

// 刘海屏判断
#define iPhoneX (ABS(MAX(CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)) / MIN(CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)) – 896 / 414.0) < 0.01 || ABS(MAX(CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)) / MIN(CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)) – 812 / 375.0) < 0.01)
1
2
PS:用小于 0.01 是为了避免精度问题,以及 iPhone 12 系列和前面刘海屏手机的纵横比存在一定误差。

相关常用宏
既然有了刘海屏的判断, 后面的工作就变得简单起来了。下面是我在编写相关代码的时候所写的宏,有需要的同学可以参考一下。

// 屏幕宽度
#define ScreenWidth [UIScreen mainScreen].bounds.size.width

// 屏幕高度
#define ScreenHeight [UIScreen mainScreen].bounds.size.height

// 状态栏高度
#define StatusBar_Height (iPhoneX ? 44.0f : 20.0f)

// 导航栏高度
#define NaviBar_Height (44.0f)

// 状态栏+导航栏高度
#define StatusBar_NaviBar_Height (StatusBar_Height + NaviBar_Height)

// TabBar高度
#define TabBar_Height (iPhoneX ? 49.0f + 34.0f : 49.0f)