从 prefersStatusBarHidden 检测屏幕缺口

Detect screen notch from prefersStatusBarHidden

Whosebug 上有很多关于检测 iPhone 设备是否在屏幕上有凹口的问题,例如 。答案几乎都是推荐使用topwindow的safeAreaInsets属性。我已经在我的应用程序中使用它来确定是否应该显示状态栏,来自当前显示的视图控制器的 prefersStatusBarHidden 方法。我想在有缺口时显示状态栏,但在没有缺口时不显示。它在我的所有测试中都运行良好,但对于某些客户来说,状态栏有时会消失,即使他们使用的是带缺口的设备(iPhone 12 Pro Max)。

我调查了一下,我认为问题可能是由对 safeAreaInsets 的递归调用引起的,请参见以下调用堆栈:

有点道理。为了确定安全区域需要多大,iOS 需要知道是否需要显示状态栏。因此,它调用可见视图控制器的 prefersStatusBarHidden,然后使用安全区域确定....

尽管递归调用,它在测试中仍然对我有效,但如前所述,它有时对某些用户失败。我需要使用 prefersStatusBarHidden,因为在顶层应用程序包含 UITabBarController,只有一个选项卡隐藏状态栏。其他选项卡应始终显示状态栏,与是否有缺口无关。

我考虑过使用 sysctlbyname"hw.machine" 参数检查设备类型,然后使用映射 table 获得 notch/no-notch 结果。但这有一个缺点,即映射 table 需要为每个新的 iPhone 模型更新,并且它在模拟器上不起作用,模拟器总是 returns [=44] =] 机器名.

有什么更好的解决方法吗?我可以简单地避免递归调用,但这会解决问题吗?


我现在确定缺口的代码(Objective-C):

- (bool) hasTopNotch
{
    if (@available(iOS 11.0, *)) {
        UIWindow *window = [UIApplication sharedApplication].delegate.window;
        UIEdgeInsets insets = window.safeAreaInsets;
        return insets.top >= 44;
    } else {
        return NO;
    }
}

我不熟悉 Obj-c,但它看起来像一个计算 property/function。每次访问它,它都会得到当前安全区域的inset和return一个Bool.

但问题是您随后根据该 Bool 设置 prefersStatusBarHidden。如果隐藏状态栏,安全区域会变小。然后,下次您访问 hasTopNotch 属性 时,它将 return 一个不正确的值。

相反,我所做的是检查安全区域一次且仅一次 应用程序启动。您用户的设备永远不会改变,因此您不需要功能。在 Swift:

var deviceHasNotch = false /// outside any class

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        deviceHasNotch = window?.safeAreaInsets.bottom ?? 0 > 0 /// set it here
    }
}