'iOS: Detect if the device is iPhone X family (frameless)
In my app there is some logic for frameless devices (iPhoneX, Xs Xs max, Xr). Currently it works base on the model of the devices, so, I detect the model by DeviceKit framework.
But I want to extend this logic to future frameless devices. Probably in one year we will have some extra frameless devices. So, how can I detect if device is frameless or not? It should cover all current frameless devices and future one.
We can not rely on faceID, safeAreaInset, screen height or size. So, then what?
Solution 1:[1]
You could "fitler" for the top notch, something like:
var hasTopNotch: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
}
return false
}
Solution 2:[2]
Swift 5, iOS 14 supported
Thanks to @Tanin and @DominicMDev, since keyWindow was deprecated in iOS 13
and the iPad Pro has non-zero safeAreaInsets
, this works fine for me.
(Already tested on iPhone 8
, iPhone 11 Pro
and iPad Pro (11-inch)(2nd gen)
Simulators)
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
}
}
}
Solution 3:[3]
Since keyWindow was deprecated in iOS 13
, based on the solution to find keyWindow
from here, this one works for me
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
}
}
Solution 4:[4]
This is valid for any orientation. No need to worry about iOS version before 11.0 since iPhone X minimum version is 11.0. Source
extension UIDevice {
var hasNotch: Bool {
if #available(iOS 11.0, *) {
return UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0 > 0
}
return false
}
}
Solution 5:[5]
I am doing it like this because the iPadPro has non-zero 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
}
}
}
Solution 6:[6]
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
}
}
}
Using by
if UIDevice.current.hasNotch
{
//... consider notch
}
else
{
//... don't have to consider notch
}
Solution 7:[7]
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
}
}
Solution 8:[8]
This way you can cover all the orientations:
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
}
Solution 9:[9]
Since ??Device Orientation != Interface Orientation??
My final code is:
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
}
}
}
Solution 10:[10]
safeTop = UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0
if safeTop > 20 {
print("Big family")
} else {
print("Before X")
}
Swift 5.0 The most simple to understand and adapt to your conditions.
Solution 11:[11]
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
}
}
Solution 12:[12]
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
}
Solution 13:[13]
If you want to check the notch in your UIViewController just set up isNotch flag in the method viewSafeAreaInsetsDidChange()
open override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
isNotch = true
}
I've tested it on Simulator only. It is called only in X devices in all orientations.
Solution 14:[14]
extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
}
}
use 'UIDevice.current.hasNotch' will return true or false
Swift 5
var hasNotch: Bool {
let bottom = UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0
return bottom > 0
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow