iOS:检测设备是否为iPhone X系列(无框)

iOS: Detect if the device is iPhone X family (frameless)

在我的应用程序中有一些针对无框设备(iPhoneX、Xs Xs max、Xr)的逻辑。目前它基于设备模型工作,因此,我通过 DeviceKit 框架检测模型。

但我想将此逻辑扩展到未来的无框设备。可能在一年内我们将有一些额外的无框设备。那么,如何检测设备是否无框?它应该涵盖所有当前和未来的无框设备。

我们不能依赖 faceID、safeAreaInset、屏幕高度或尺寸。那么,然后呢?

您可以 "fitler" 获得一流的成绩,例如:

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
}

这对任何方向都有效。 无需担心 iOS 11.0 之前的版本,因为 iPhone X 最低版本为 11.0。 Source

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
}

Swift 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 {[=10=].isKeyWindow}.first
            return keyWindow?.safeAreaInsets.bottom ?? 0 > 0
        }
        return false
    }

}

Swift5个,iOS14个支持

感谢@Tanin 和@DominicMDev,因为 keyWindow was deprecated in iOS 13the iPad Pro has non-zero safeAreaInsets,这对我来说很好。

(已在 iPhone 8iPhone 11 ProiPad 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({[=10=].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
        }
    }
}
extension UIDevice {
    var hasNotch: Bool {
        let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
        return bottom > 0
    }
}

使用 'UIDevice.current.hasNotch' 将 return 正确或错误

Swift 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")
}

Swift 5.0 最简单易懂,适合你的情况。

因为 ⚠️设备方向 != 界面方向 ⚠️

我的最终代码是:

extension UIDevice {
    var hasNotch: Bool {
        guard #available(iOS 11.0, *), let window = UIApplication.shared.windows.filter({[=10=].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({[=10=].isKeyWindow}).first else { return false }

    return window.safeAreaInsets.top >= 44
}

如果您想检查 UIViewController 中的缺口,只需在方法 viewSafeAreaInsetsDidChange() 中设置 isNotch 标志

open override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    isNotch = true
}

我只在模拟器上测试过。它仅在所有方向的 X 设备中调用。