提问人:Mark cubn 提问时间:9/19/2018 更新时间:11/6/2023 访问量:32205
iOS:检测设备是否为 iPhone X 系列(无框)
iOS: Detect if the device is iPhone X family (frameless)
问:
在我的应用程序中,有一些无框设备(iPhoneX、Xs、Xs、max、Xr)的逻辑。目前,它基于设备的模型工作,因此,我通过DeviceKit框架检测模型。
但我想将这种逻辑扩展到未来的无框设备。可能在一年内,我们将拥有一些额外的无框设备。那么,如何检测设备是否为无框?它应该涵盖所有当前的无框设备和未来的无框设备。
我们不能依赖 faceID、safeAreaInset、屏幕高度或大小。那么,然后呢?
答:
您可以“过滤”出一流的内容,例如:
var hasTopNotch: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
}
return false
}
评论
这样,您可以涵盖所有方向:
var hasTopNotch: Bool
{
if #available(iOS 11.0, *) {
var safeAreaInset: CGFloat?
if (UIApplication.shared.statusBarOrientation == .portrait) {
safeAreaInset = UIApplication.shared.delegate?.window??.safeAreaInsets.top
}
else if (UIApplication.shared.statusBarOrientation == .landscapeLeft) {
safeAreaInset = UIApplication.shared.delegate?.window??.safeAreaInsets.left
}
else if (UIApplication.shared.statusBarOrientation == .landscapeRight) {
safeAreaInset = UIApplication.shared.delegate?.window??.safeAreaInsets.right
}
return safeAreaInset ?? 0 > 24
}
return false
}
这适用于任何方向。 无需担心 11.0 之前的 iOS 版本,因为 iPhone X 最低版本为 11.0。源
extension UIDevice {
var hasNotch: Bool {
if #available(iOS 11.0, *) {
return UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0 > 0
}
return false
}
}
评论
我这样做是因为 iPadPro 具有非零 safeAreaInsets。
extension UIDevice {
/// Returns 'true' if the device has a notch
var hasNotch: Bool {
guard #available(iOS 11.0, *), let window = UIApplication.shared.keyWindow else { return false }
let orientation = UIApplication.shared.statusBarOrientation
if orientation.isPortrait {
return window.safeAreaInsets.top >= 44
} else {
return window.safeAreaInsets.left > 0 || window.safeAreaInsets.right > 0
}
}
}
extension UIDevice {
var hasNotch: Bool
{
if #available(iOS 11.0, *)
{
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
} else
{
// Fallback on earlier versions
return false
}
}
}
使用方式
if UIDevice.current.hasNotch
{
//... consider notch
}
else
{
//... don't have to consider notch
}
斯威夫特 5
var hasNotch: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
} else {
return false
}
}
由于,基于从这里找到的解决方案,这个对我有用keyWindow was deprecated in iOS 13
keyWindow
extension UIDevice {
var hasNotch: Bool {
if #available(iOS 11.0, *) {
let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
return keyWindow?.safeAreaInsets.bottom ?? 0 > 0
}
return false
}
}
评论
keyWindow?.safeAreaInsets.bottom == 20
支持 Swift 5、iOS 14
感谢 @Tanin 和 @DominicMDev,因为 和 ,这对我来说很好用。keyWindow was deprecated in iOS 13
the iPad Pro has non-zero safeAreaInsets
(已在 和 模拟器上测试)iPhone 8
iPhone 11 Pro
iPad Pro (11-inch)(2nd gen)
extension UIDevice {
/// Returns `true` if the device has a notch
var hasNotch: Bool {
guard #available(iOS 11.0, *), let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first else { return false }
if UIDevice.current.orientation.isPortrait {
return window.safeAreaInsets.top >= 44
} else {
return window.safeAreaInsets.left > 0 || window.safeAreaInsets.right > 0
}
}
}
使用示例:
print(UIDevice.current.hasNotch)
评论
isPortrait
isPortrait
UIDevice.current.orientation.isPortrait
window.safeAreaInsets.left
extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
}
}
使用 'UIDevice.current.hasNotch' 将返回 true 或 false
斯威夫特 5
var hasNotch: Bool {
let bottom = UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0
return bottom > 0
}
评论
safeTop = UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0
if safeTop > 20 {
print("Big family")
} else {
print("Before X")
}
斯威夫特 5.0最容易理解和适应您的条件。
评论
由于⚠️设备方向 != 接口方向⚠️
我的最终代码是:
extension UIDevice {
var hasNotch: Bool {
guard #available(iOS 11.0, *), let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first else { return false }
//if UIDevice.current.orientation.isPortrait { //Device Orientation != Interface Orientation
if let o = windowInterfaceOrientation?.isPortrait, o == true {
return window.safeAreaInsets.top >= 44
} else {
return window.safeAreaInsets.left > 0 || window.safeAreaInsets.right > 0
}
}
private var windowInterfaceOrientation: UIInterfaceOrientation? {
if #available(iOS 13.0, *) {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
} else {
return UIApplication.shared.statusBarOrientation
}
}
}
var hasNotch: Bool {
if #available(iOS 11.0, *) {
if UIApplication.shared.windows.count == 0 { return false } // Should never occur, but…
let top = UIApplication.shared.windows[0].safeAreaInsets.top
return top > 20 // That seem to be the minimum top when no notch…
} else {
// Fallback on earlier versions
return false
}
}
评论
func isIphoneX() -> Bool {
guard #available(iOS 11.0, *),
let window = UIApplication.shared.windows.filter({$0.isKeyWindow}).first else { return false }
return window.safeAreaInsets.top >= 44
}
如果要检查 UIViewController 中的缺口,只需在方法 viewSafeAreaInsetsDidChange() 中设置 isNotch 标志
open override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
isNotch = true
}
我只在模拟器上测试过它。它仅在所有方向的 X 设备中被调用。
当你有
场景委托.swift
类。这将起作用
extension UIDevice {
var hasNotch: Bool {
if #available(iOS 11.0, *) {
if UIApplication.shared.windows.count == 0 { return false } // Should never occur, but…
let top = UIApplication.shared.windows[0].safeAreaInsets.top
return top > 20 // That seems to be the minimum top when no notch…
} else {
// Fallback on earlier versions
return false
}
}
}
对于使用场景委托的 SwiftUI:
var hasNotch: Bool {
guard let scene = UIApplication.shared.connectedScenes.first,
let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate,
let window = windowSceneDelegate.window else {
return false
}
return window?.safeAreaInsets.top ?? 0 > 20
}
}
我已经建立在 Saafo 的答案之上,现在在 iOS 15.0 中已弃用,如果方向未知,我也默认检查顶部插入UIApplication.shared.windows
extension UIDevice {
var hasNotch: Bool {
guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return false
}
guard let firstWindow = firstScene.windows.filter({$0.isKeyWindow}).first else {
return false
}
if UIDevice.current.orientation.isPortrait ||
UIDevice.current.orientation == .unknown {
return firstWindow.safeAreaInsets.top >= 44
} else {
return firstWindow.safeAreaInsets.left > 0 || firstWindow.safeAreaInsets.right > 0
}
}
}
根据提供的所有答案,我需要另一种方式。我创建了一个具有所有必要接口细微差别的枚举,并在变量中返回所需的类型:
enum DeviceInterfaceType {
case dynamicIsland
case notch
case none
}
extension UIDevice {
var interfaceType: DeviceInterfaceType {
guard let window = (UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }.first { $0.isKeyWindow}) else {
return .none
}
if window.safeAreaInsets.top >= 51 && userInterfaceIdiom == .phone {
return .dynamicIsland
} else if window.safeAreaInsets.top >= 44 {
return .notch
} else if window.safeAreaInsets.left > 0 || window.safeAreaInsets.right > 0 {
return .none
}
return .none
}
}
函数调用
switch UIDevice.current.interfaceType {
case .dynamicIsland:
//
case .notch:
//
case .none:
//
}
评论