问题描述
StackOverflow 上有很多关于检测 iPhone 设备屏幕上是否有缺口的问题,例如 this one。答案几乎总是建议使用顶部窗口的 safeAreaInsets
属性。我已经在我的应用程序中使用它来确定是否应该显示状态栏,从当前显示的视图控制器的 prefeRSStatusBarHidden
方法。我想在有缺口时显示状态栏,但没有时则不显示。它在我的所有测试中都运行良好,但对于某些客户而言,状态栏有时会消失,即使他们使用的是带有缺口的设备(iPhone 12 Pro Max)。
我查看了它,我认为问题可能是由对 safeAreaInsets
的递归调用引起的,请参阅以下调用堆栈:
有点道理。为了确定安全区域需要多大,iOS 需要知道状态栏是否需要显示。因此,它调用可见视图控制器的 prefeRSStatusBarHidden
,然后使用安全区域来确定....
尽管进行了递归调用,但它在测试中仍然对我有用,但如上所述,有时对某些用户来说它会失败。我需要使用 prefeRSStatusBarHidden
,因为在顶级应用程序包含一个 UITabBarController
,只有一个选项卡隐藏了状态栏。其他选项卡应始终显示状态栏,与是否有缺口无关。
我考虑过使用 sysctlbyname
和 "hw.machine"
参数检查设备类型,然后使用映射表来获得缺口/无缺口结果。但这有一个缺点,即需要为每个新 iPhone 型号更新映射表,而且它在模拟器上不起作用,模拟器总是返回 Mac 机器名称。
任何想法如何以更好的方式解决这个问题?我可以简单地避免递归调用,但这能解决问题吗?
我现在确定缺口的代码(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 但它看起来像一个计算属性/函数。每次访问它都会获取当前的安全区域 inset 并返回一个 Bool。
但问题是您随后要根据该布尔值设置 prefersStatusBarHidden
。如果隐藏状态栏,安全区域会变小。然后,下次您访问 hasTopNotch
属性时,它将返回错误值。
相反,我所做的是在应用启动时一次且仅一次检查安全区域。您用户的设备永远不会改变,因此您不需要函数。在 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
}
}