检测设备是否为iPhone X

我的iOS应用程序为UINavigationBar使用了自定义高度,这导致了新的iPhone X上的一些问题。

有人已经知道如何可靠的检测程序(在Objective-C中),如果一个应用程序正在iPhone X上运行?

编辑:

当然,检查屏幕的大小是可能的,但是,我想知道是否有一些“内置”的方法,如TARGET_OS_IPHONE来检测iOS…

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
if (screenSize.height == 812)
NSLog(@"iPhone X");
}

编辑2:

我不认为,我的问题是一个重复的关联问题。当然,有一些方法可以“测量”当前设备的不同属性,并使用结果来决定使用哪种设备。然而,这并不是我在第一篇编辑中试图强调的问题的实际意义。

实际的问题是:“是否有可能直接检测当前设备是否是iPhone X(例如通过某些SDK功能),还是我必须使用间接测量?”?

根据目前给出的答案,我假设答案是“不,没有直接的方法。测量是要走的路。”

221620 次浏览
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)

根据你的问题,答案是否定的。没有直接的方法。欲了解更多信息,您可以在这里获得信息:

而且

iPhone X的高度是2436像素

设备屏幕尺寸和分辨率:

enter image description here

设备屏幕尺寸和方向:

enter image description here

Swift 3及后续版本:

if UIDevice().userInterfaceIdiom == .phone {
switch UIScreen.main.nativeBounds.height {
case 1136:
print("iPhone 5 or 5S or 5C")
        

case 1334:
print("iPhone 6/6S/7/8")
        

case 1920, 2208:
print("iPhone 6+/6S+/7+/8+")
        

case 2436:
print("iPhone X/XS/11 Pro")
        

case 2688:
print("iPhone XS Max/11 Pro Max")
        

case 1792:
print("iPhone XR/ 11 ")
        

default:
print("Unknown")
}
}

objective - c:

if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
switch ((int)[[UIScreen mainScreen] nativeBounds].size.height) {
case 1136:
printf("iPhone 5 or 5S or 5C");
break;


case 1334:
printf("iPhone 6/6S/7/8");
break;


case 1920:
case 2208:
printf("iPhone 6+/6S+/7+/8+");
break;


case 2436:
printf("iPhone X/XS/11 Pro");
break;


case 2688:
printf("iPhone XS Max/11 Pro Max");
break;


case 1792:
printf("iPhone XR/ 11 ");
break;


default:
printf("Unknown");
break;
}
}

Xamarin.iOS:

if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) {
if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1136) {
Console.WriteLine("iPhone 5 or 5S or 5C");
} else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1334) {
Console.WriteLine("iPhone 6/6S/7/8");
} else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1920 || (UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2208) {
Console.WriteLine("iPhone 6+/6S+/7+/8+");
} else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2436) {
Console.WriteLine("iPhone X, XS, 11 Pro");
} else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2688) {
Console.WriteLine("iPhone XS Max, 11 Pro Max");
} else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1792) {
Console.WriteLine("iPhone XR, 11");
} else {
Console.WriteLine("Unknown");
}
}

根据你的问题如下:

或者使用screenSize.height作为float 812.0f而不是int 812

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// 812.0 on iPhone X, XS
// 896.0 on iPhone XS Max, XR.


if (screenSize.height >= 812.0f)
NSLog(@"iPhone X");
}

有关更多信息,您可以参考以下页面的iOS人机界面指南:

斯威夫特:

topNotch检测:

如果有人考虑使用notch来检测iPhoneX,请注意"landscape"所有iphone都是一样的。

var hasTopNotch: Bool {
if #available(iOS 13.0,  *) {
return UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.safeAreaInsets.top ?? 0 > 20
}else{
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
}


return false
}

objective - c:

- (BOOL)hasTopNotch {
if (@available(iOS 13.0, *)) {
return [self keyWindow].safeAreaInsets.top > 20.0;
}else{
return [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top > 20.0;
}
return  NO;
}


- (UIWindow*)keyWindow {
UIWindow        *foundWindow = nil;
NSArray         *windows = [[UIApplication sharedApplication]windows];
for (UIWindow   *window in windows) {
if (window.isKeyWindow) {
foundWindow = window;
break;
}
}
return foundWindow;
}

更新:

不要使用userInterfaceIdiom属性来标识设备类型,正如userInterfaceIdiom的文档解释的那样:

对于通用应用程序,您可以使用此属性为特定类型的设备定制应用程序的行为。例如,iPhone和iPad设备具有不同的屏幕尺寸,因此您可能希望根据当前设备的类型创建不同的视图和控件。

也就是说,这个属性只是用来识别正在运行的应用程序的视图样式。然而,iPhone应用程序(不是通用的)可以通过应用程序商店安装在iPad设备上,在这种情况下,userInterfaceIdiom也将返回UIUserInterfaceIdiomPhone

正确的方法是通过uname获取机器名。详情请查阅以下资料:

是的,有可能。 下载UIDevice-Hardware扩展(或通过CocoaPod 'UIDevice-Hardware'安装),然后使用:

NSString* modelID = [[[UIDevice currentDevice] modelIdentifier];
BOOL isIphoneX = [modelID isEqualToString:@"iPhone10,3"] || [modelID isEqualToString:@"iPhone10,6"];

注意,这不能在模拟器中工作,只能在实际设备上工作。

另一种可能是,在iOS 11和iOS 12上都能运行,因为iPhone X是唯一一款顶部有凹槽、嵌框为44的手机。这就是我在这里真正发现的:

objective - c:

    BOOL iPhoneX = NO;
if (@available(iOS 11.0, *)) {
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
if (mainWindow.safeAreaInsets.top > 24.0) {
iPhoneX = YES;
}
}

斯威夫特4:

/// Has safe area
///
/// with notch: 44.0 on iPhone X, XS, XS Max, XR.
///
/// without notch: 20.0 on iPhone 8 on iOS 12+.
///
static var hasSafeArea: Bool {
guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 24 else {
return false
}
return true
}

当然,如果你是在横向,你可能需要检查左右安全区域的嵌入。

Edit: _window是AppDelegate的UIWindow,这个检查在应用didFinishLaunchingWithOptions中完成。

iOS 12的答案更新,以检查顶部>是否为24,而不是顶部> 0。

编辑:在模拟器中,您可以选择硬件,切换通话状态栏。这样做表明,在通话时,iOS 11的iPhone X或iOS 12的iPhone XS上的状态栏高度没有变化。在这两种情况下,改变的只是时间图标,它的背景都是绿色的。这是一张快照:

enter image description here

你可以这样做,根据尺寸来检测iPhone X设备。

斯威夫特

if UIDevice().userInterfaceIdiom == .phone && UIScreen.main.nativeBounds.height == 2436 {
//iPhone X
}

目标- C

if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && UIScreen.mainScreen.nativeBounds.size.height == 2436)  {
//iPhone X
}

enter image description here

,

这是不够的。如果苹果发布的下一代iPhone与iPhone x的尺寸相同,那么最好的方法是使用硬件字符串来检测设备。

对于较新的设备硬件字符串如下。

iPhone 8 - iPhone10 1iPhone 10 4

iPhone 8 Plus - iPhone10 2iPhone 10 5

iPhone X - iPhone10 3iPhone10 6

你不应该假设苹果发布的唯一具有不同UINavigationBar高度的设备将是iPhone x。尝试使用更通用的解决方案来解决这个问题。如果你想让条形图总是比默认高度大20px,你的代码应该为条形图的高度增加20px,而不是将其设置为64px (44px + 20px)。

所有这些基于尺寸的答案都很容易在未来的设备上出现错误行为。它们今天还能用,但如果明年有一款同样大小的iPhone,但在玻璃下面有摄像头等,所以没有“缺口”呢?如果唯一的选择是更新应用程序,那么这对你和你的客户来说都是一个糟糕的解决方案。

您还可以检查硬件型号字符串,如“iPhone10,1”,但这是有问题的,因为有时苹果会为世界各地的不同运营商发布不同的型号。

正确的方法是重新设计顶部布局,或者解决自定义导航栏高度的问题(这是我关注的重点)。但是,如果你决定不做这两件事中的任何一件,要意识到无论你正在做什么,都是为了让这个今天< em > < / em >工作而进行的hack,并且你需要在某个时候纠正它,可能是多次,以保持hack工作。

根据@saswanb的回复,这是Swift 4的版本:

var iphoneX = false
if #available(iOS 11.0, *) {
if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
iphoneX = true
}
}

检查设备型号/机器名称,不要在你的代码中直接使用点/像素计数,它是< >强硬编码< / >强,对设备硬件没有意义,设备型号是要匹配的设备类型的唯一标识符

#import <sys/utsname.h>


NSString* deviceName()
{
struct utsname systemInfo;
uname(&systemInfo);


return [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
}

结果:

@"iPhone10,3" on iPhone X (CDMA)
@"iPhone10,6" on iPhone X (GSM)

参考这个答案

完整的代码实现:

#import <sys/utsname.h>


NSString * GetDeviceModel(void)
{
static dispatch_once_t onceToken;
static NSString *strModelID = nil;


dispatch_once(&onceToken, ^{
#if TARGET_IPHONE_SIMULATOR
strModelID = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
#else
struct utsname systemInfo;


uname(&systemInfo);
strModelID = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
#endif
});


return strModelID;
}


// See the `Hardware strings` in https://en.wikipedia.org/wiki/List_of_iOS_devices
BOOL IsiPhoneX(void)
{
NSString *strModelID = GetDeviceModel();


return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"];
}


BOOL IsNotchiPhone(void)
{
NSArray<NSString *> *notchiModels = @[
@"iPhone10,3", @"iPhone10,6", // iPhone X
@"iPhone11,2", @"iPhone11,4", @"iPhone11,6", // iPhone XS (Max)
@"iPhone11,8", // iPhone XR
@"iPhone12,1", @"iPhone12,3", @"iPhone12,5", // iPhone 11 (Pro (Max))
@"iPhone13,1", @"iPhone13,2", @"iPhone13,3", @"iPhone13,4", // iPhone 12 ([mini]|[Pro (Max)])
];


return [notchiModels containsObject:GetDeviceModel()];
}
#define IS_IPHONE        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS  (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_IPHONE_X      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)

定义IS_IPHONE_X (IS_IPHONE &[[UIScreen mainScreen] bounds].size。身高== 812.0)

#define IS_IPHONE_XS      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)
#define IS_IPHONE_X_MAX      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 896.0)
#define IS_RETINA        ([[UIScreen mainScreen] scale] >= 2.0) // 3.0 for iPhone X, 2.0 for others


#define IS_IPAD_DEVICE   [(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"]

注意:-小心,它只适用于纵向方向

SWIFT 4+ Answer

iPhone X, XR, XS, XSMAX, 11 Pro, 11 Pro Max:

注:需实物设备进行测试

参考< a href = " https://stackoverflow.com/a/26962452/7576100 " > < / >

 let deviceType = UIDevice.current.modelName
switch deviceType {
case "iPhone10,3", "iPhone10,6":
print("iPhoneX")
case "iPhone11,2":
print("iPhone XS")
case "iPhone11,4":
print("iPhone XS Max")
case "iPhone11,6":
print("iPhone XS Max China")
case "iPhone11,8":
print("iPhone XR")
case "iPhone12,3":
print("iPhone 11 Pro")
case "iPhone12,5":
print("iPhone 11 Pro Max")
default:
break
}


extension UIDevice {
var modelName: String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
}

有几个原因让我们想知道这个设备是什么。

  1. 您可以检查设备的高度(和宽度)。这对布局很有用,但如果你想知道确切的设备,你通常不想这样做。

  2. 出于布局的目的,你也可以使用UIView.safeAreaInsets

  3. 如果你想显示设备名称,例如,为了诊断目的而包含在电子邮件中,在使用sysctl ()检索设备模型后,你可以使用等价的方法来计算名称:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    
    iPhone X
    
- (BOOL)isIphoneX {
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
if(topPadding>0) {
return YES;
}
else {
return NO;
}
}
else {
return NO;
}
}

由于一个原因,所有使用height的答案都只是故事的一半。如果你想在设备方向为landscapeLeftlandscapeRight时这样检查,检查将会失败,因为heightwidth交换了。

这就是为什么我的解决方案看起来像这样在Swift 4.0:

extension UIScreen {
///
static var isPhoneX: Bool {
let screenSize = UIScreen.main.bounds.size
let width = screenSize.width
let height = screenSize.height
return min(width, height) == 375 && max(width, height) == 812
}
}

我知道这只是一个斯威夫特解决方案,但它可以帮助某些人。

我在每个项目中都有globals.swift和一些代码来简化我的生活,我总是添加的一件事是ScreenSizehasNotch,以便轻松检测手机类型和尺寸:

struct ScreenSize {
static let width = UIScreen.main.bounds.size.width
static let height = UIScreen.main.bounds.size.height
}


var hasNotch: Bool {
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 0
}

然后使用它:

if hasNotch {
print("This executes on all phones with a notch")
}

您需要根据实际需要对iPhone X进行不同的检测。

用于处理顶级(状态栏,导航栏)等。

class var hasTopNotch: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
// with notch: 44.0 on iPhone X, XS, XS Max, XR.
// without notch: 24.0 on iPad Pro 12.9" 3rd generation, 20.0 on iPhone 8 on iOS 12+.
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 24
}
return false
}

用于处理底部home指示器(标签栏)等。

class var hasBottomSafeAreaInsets: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
// with home indicator: 34.0 on iPhone X, XS, XS Max, XR.
// with home indicator: 20.0 on iPad Pro 12.9" 3rd generation.
return UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0 > 0
}
return false
}

背景大小,全屏功能等。

class var isIphoneXOrBigger: Bool {
// 812.0 on iPhone X, XS.
// 896.0 on iPhone XS Max, XR.
return UIScreen.main.bounds.height >= 812
}
注意:最终与UIDevice.current.userInterfaceIdiom == .phone
混合 注意:此方法需要有一个LaunchScreen故事板或适当的LaunchImages

背景比例,滚动功能等。

class var isIphoneXOrLonger: Bool {
// 812.0 / 375.0 on iPhone X, XS.
// 896.0 / 414.0 on iPhone XS Max, XR.
return UIScreen.main.bounds.height / UIScreen.main.bounds.width >= 896.0 / 414.0
}

注意:此方法需要有一个LaunchScreen故事板或适当的launchimage

用于分析,统计,跟踪等。

获取机器标识符并将其与文档值进行比较:

class var isIphoneX: Bool {
var size = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
let model = String(cString: machine)
return model == "iPhone10,3" || model == "iPhone10,6"
}

要将模拟器作为有效的iPhone X包含在分析中:

class var isIphoneX: Bool {
let model: String
if TARGET_OS_SIMULATOR != 0 {
model = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
} else {
var size = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
model = String(cString: machine)
}
return model == "iPhone10,3" || model == "iPhone10,6"
}

要包括iPhone XS、XS Max和XR,只需查找以“iPhone11”开头的型号:

return model == "iPhone10,3" || model == "iPhone10,6" || model.starts(with: "iPhone11,")

支持faceID

import LocalAuthentication
/// will fail if user denies canEvaluatePolicy(_:error:)
class var canUseFaceID: Bool {
if #available(iOS 11.0, *) {
return LAContext().biometryType == .typeFaceID
}
return false
}
struct ScreenSize {
static let width = UIScreen.main.bounds.size.width
static let height = UIScreen.main.bounds.size.height
static let maxLength = max(ScreenSize.width, ScreenSize.height)
static let minLength = min(ScreenSize.width, ScreenSize.height)
static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
}


struct DeviceType {
static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength < 568.0
static let iPhone5orSE = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 568.0
static let iPhone678 = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 667.0
static let iPhone678p = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 736.0
static let iPhoneX = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 812.0


static let IS_IPAD              = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1024.0
static let IS_IPAD_PRO          = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1366.0
}

通常,程序员需要它来约束到顶部或底部,所以这些方法可以提供帮助

static func extraTop() -> CGFloat {


var top: CGFloat = 0


if #available(iOS 11.0, *) {


if let t = UIApplication.shared.keyWindow?.safeAreaInsets.top {
top = t
}
}
return top
}


static func extraBottom() -> CGFloat {


var bottom: CGFloat = 0


if #available(iOS 11.0, *) {


if let b = UIApplication.shared.keyWindow?.safeAreaInsets.bottom {
bottom = b
}
}
return bottom
}

在iPhone X之前,这些方法返回:0

iPhone X: 44和34

然后只需将这些额外的内容添加到顶部或底部约束

在看完所有的答案后,我最后是这么做的:

解决方案(兼容Swift 4.1)

extension UIDevice {
static var isIphoneX: Bool {
var modelIdentifier = ""
if isSimulator {
modelIdentifier = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
} else {
var size = 0
sysctlbyname("hw.machine", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.machine", &machine, &size, nil, 0)
modelIdentifier = String(cString: machine)
}


return modelIdentifier == "iPhone10,3" || modelIdentifier == "iPhone10,6"
}


static var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0
}
}

使用

if UIDevice.isIphoneX {
// is iPhoneX
} else {
// is not iPhoneX
}

请注意

精准医疗 Swift 4.1你可以检查应用程序是否在模拟器上运行,就像这样:

TARGET_OS_SIMULATOR != 0

从Swift 4.1开始,你可以使用目标环境平台条件检查应用程序是否在模拟器上运行:

#if targetEnvironment(simulator)
return true
#else
return false
#endif

(旧的方法仍然有效,但这个新方法更经得起未来的考验)

我详细阐述了你对其他人的回答,并在UIDevice上进行了快速扩展。我喜欢快速枚举和“一切井然有序”。雾化。我已经创建了解决方案,工作在设备和放大器;模拟器。

< p >优势: -简单的界面,使用方法,例如UIDevice.current.isIPhoneX - UIDeviceModelType enum让你能够轻松扩展模型特定的功能和常数,你想在你的应用程序中使用,例如:cornradius

< p >劣势: -这是特定于型号的解决方案,而不是特定于分辨率的解决方案-例如,如果苹果将生产另一种具有相同规格的型号,这将无法正常工作,你需要添加另一种型号才能正常工作=>你需要更新你的应用程序
extension UIDevice {


enum UIDeviceModelType : Equatable {


///iPhoneX
case iPhoneX


///Other models
case other(model: String)


static func type(from model: String) -> UIDeviceModelType {
switch model {
case "iPhone10,3", "iPhone10,6":
return .iPhoneX
default:
return .other(model: model)
}
}


static func ==(lhs: UIDeviceModelType, rhs: UIDeviceModelType) -> Bool {
switch (lhs, rhs) {
case (.iPhoneX, .iPhoneX):
return true
case (.other(let modelOne), .other(let modelTwo)):
return modelOne == modelTwo
default:
return false
}
}
}


var simulatorModel: String? {
guard TARGET_OS_SIMULATOR != 0 else {
return nil
}


return ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"]
}


var hardwareModel: String {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let model = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}


return model
}


var modelType: UIDeviceModelType {
let model = self.simulatorModel ?? self.hardwareModel
return UIDeviceModelType.type(from: model)
}


var isIPhoneX: Bool {
return modelType == .iPhoneX
}
}

对于那些获得2001px而不是2436px的原生边界高度的人(比如我),这是因为你使用的是iOS 11 (Xcode 8而不是Xcode 9)之前的旧SDK构建的应用程序。使用旧SDK, iOS将在iPhone X上显示应用程序的“黑盒子”,而不是将屏幕边缘扩展到顶部的“传感器缺口”。这减少了屏幕大小,这就是为什么该属性返回2001而不是2436。

如果您只对设备检测感兴趣,最简单的解决方案是检查两个大小。我使用这种方法来检测FaceID,同时使用一个旧的Xcode SDK,它没有ENUM值指定生物特征类型。在这种情况下,使用屏幕高度进行设备检测似乎是知道设备是否具有FaceID还是TouchID的最佳方法,而无需更新Xcode。

我依靠状态栏框架高度来检测它是否是iPhone X:

if UIApplication.shared.statusBarFrame.height >= CGFloat(44) {
// It is an iPhone X
}

这是为应用程序unportrait。您还可以根据设备方向检查大小。另外,在其他iphone上,状态栏可能是隐藏的,因此帧高度为0。在iPhone X上,状态栏永远不会隐藏。

为了快速解决问题,我喜欢这样:

let var:CGFloat = (UIDevice.current.userInterfaceIdiom == .phone && UIScreen.main.nativeBounds.height == 2436) ? <iPhoneX> : <AllOthers>

Swift 3 + 4:

不需要任何设备大小的像素值

//UIApplication+SafeArea.swift


extension UIApplication {


static var isDeviceWithSafeArea:Bool {


if #available(iOS 11.0, *) {
if let topPadding = shared.keyWindow?.safeAreaInsets.bottom,
topPadding > 0 {
return true
}
}


return false
}
}

例子:

if UIApplication.isDeviceWithSafeArea {
//e.g. change the frame size height of your UITabBar
}

我正在使用Peter Kreinz的代码(因为它是干净的,做了我需要的),但后来我意识到它只在设备在纵向上工作(因为顶部填充将在顶部,显然) 所以我创建了一个扩展来处理所有方向与它各自的填充,而不依赖于屏幕大小:

extension UIDevice {


var isIphoneX: Bool {
if #available(iOS 11.0, *), isIphone {
if isLandscape {
if let leftPadding = UIApplication.shared.keyWindow?.safeAreaInsets.left, leftPadding > 0 {
return true
}
if let rightPadding = UIApplication.shared.keyWindow?.safeAreaInsets.right, rightPadding > 0 {
return true
}
} else {
if let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 0 {
return true
}
if let bottomPadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom, bottomPadding > 0 {
return true
}
}
}
return false
}


var isLandscape: Bool {
return UIDeviceOrientationIsLandscape(orientation) || UIInterfaceOrientationIsLandscape(UIApplication.shared.statusBarOrientation)
}


var isPortrait: Bool {
return UIDeviceOrientationIsPortrait(orientation) || UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)
}


var isIphone: Bool {
return self.userInterfaceIdiom == .phone
}


var isIpad: Bool {
return self.userInterfaceIdiom == .pad
}
}

在你的电话网站上

let res = UIDevice.current.isIphoneX

我最近不得不解决同样的问题。虽然这个问题得到了明确的答案(“否”),但这可能会帮助其他需要iPhone X特定布局行为的人。

我对这款设备是不是iPhone x并不感兴趣,我感兴趣的是它的显示屏是否有凹槽。

private static var hasNotchedDisplay: Bool {
if let window = UIApplication.shared.keyWindow {
return (window.compatibleSafeAreaInsets.top > 20.0 || window.compatibleSafeAreaInsets.left > 0.0 || window.compatibleSafeAreaInsets.right > 0.0)
}


return false
}

你也可以写一个hasOnScreenHomeIndicator变量沿着同样的行(尽管检查底部的安全区域,也许?)

上面使用了我在UIView上的扩展,方便地访问iOS 10和更早的安全区域嵌入。

@objc public extension UIView {
@objc public var compatibleSafeAreaInsets: UIEdgeInsets {
if #available(iOS 11.0, *) {
return safeAreaInsets
} else {
return .zero
}
}


@objc public var compatibleSafeAreaLayoutGuide: UILayoutGuide {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide
} else {
return layoutMarginsGuide
}
}
}

检测设备是否为iPhone X的最佳和最简单的方法是,

https://github.com/stephanheilner/UIDevice-DisplayName

var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8 , value != 0 else { return identifier}
return identifier + String(UnicodeScalar(UInt8(value)))}

标识符为iPhone X的“iPhone10,3”或“iPhone10,6”

不要像其他解决方案建议的那样使用屏幕像素大小,这很糟糕,因为它可能会导致未来设备出现假阳性;将不工作,如果UIWindow尚未渲染(AppDelegate),将不工作在景观应用程序,并可以失败在模拟器上,如果比例设置。

相反,我已经为此目的做了一个宏,它非常容易使用,并依赖于硬件标志来防止上述问题。

编辑:更新到支持iPhoneX, iPhoneXS, iPhoneXR, iPhoneXS Max


使用方法:

if (IS_DEVICE_IPHONEX) {
//do stuff
}

是的,真的。


宏:

只要复制粘贴到任何地方,我更喜欢在我的.h文件的底部@end之后

#import <sys/utsname.h>


#if TARGET_IPHONE_SIMULATOR
#define IS_SIMULATOR YES
#else
#define IS_SIMULATOR NO
#endif


#define IS_DEVICE_IPHONEX (\
(^BOOL (void){\
NSString *__modelIdentifier;\
if (IS_SIMULATOR) {\
__modelIdentifier = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];\
} else {\
struct utsname __systemInfo;\
uname(&__systemInfo);\
__modelIdentifier = [NSString stringWithCString:__systemInfo.machine encoding:NSUTF8StringEncoding];\
}\
NSString *__iPhoneX_GSM_Identifier = @"iPhone10,6";\
NSString *__iPhoneX_CDMA_Identifier = @"iPhone10,3";\
NSString *__iPhoneXR_Identifier = @"iPhone11,8";\
NSString *__iPhoneXS_Identifier = @"iPhone11,2";\
NSString *__iPhoneXSMax_China_Identifier = @"iPhone11,6";\
NSString *__iPhoneXSMax_Other_Identifier = @"iPhone11,4";\
return ([__modelIdentifier isEqualToString:__iPhoneX_GSM_Identifier] || [__modelIdentifier isEqualToString:__iPhoneX_CDMA_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXR_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXS_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_China_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_Other_Identifier]);\
})()\
)
或者,你可以查看'DeviceKit' pod。 安装完成后,检查设备所需要做的就是:

import DeviceKit
let device = Device()
if device == .iPhoneX {
// place your code here
}

画像只有中,我使用视图帧的宽度和高度来检查:

override func viewDidLoad() {
super.viewDidLoad()


// iPhone Xr: -414 x 896
// iPhone Xs Max: -414 x 896
// iPhone X, Xs: -375 x 812


if view.frame.width == 414 && view.frame.height == 896 || view.frame.width == 375 && view.frame.height == 812  {


print("iPhone X")
} else {


print("not iPhone X")
}


}

纵向屏幕尺寸列在这里

enter image description here

更新

这个答案已经过时了,现在iPhone阵容中有更多的X系列,你要么必须在if-else中列出所有这些尺寸,要么检查设备是否有缺口会更容易。大约1.5年前,我从某个地方得到了这个答案/代码。如果我能链接到代码,我会的。

// 1. add an extension to UIDevice with this computed property
extension UIDevice {
    

var hasTopNotch: Bool {
if #available(iOS 11.0, tvOS 11.0, *) {
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
}
return false
}
}


// 2. to use in any class
override func viewDidLoad() {
super.viewDidLoad()


if UIDevice.current.hasTopNotch {


print("X series")


} else {


print("regular phone")
}
}

随着iOS 12的发布,像iPhone X这样的设备可能会被归入这一类别。

extension UIDevice {
var isPortrait: Bool {


return UIDeviceOrientationIsPortrait(orientation) ||
UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)


}


var isDeviceWith_XShape : Bool {


if self.userInterfaceIdiom == .phone {


if isPortrait
{
switch UIScreen.main.nativeBounds.height {


case 2436,2688,1792:
print("iPhone X, Xs, Xr, Xs Max")
return true
default:
print("Any other device")
return false
}
}
else
{
switch UIScreen.main.nativeBounds.width {


case 2436,2688,1792:
print("iPhone X, Xs, Xr, Xs Max")
return true
default:
print("Any other device")
return false
}
}




}
else
{
return false
}


}`

斯威夫特4/5可重用的扩展iPhone 12支持

    extension UIDevice {
    

enum `Type` {
case iPhone_5_5S_5C_SE1
case iPhone_6_6S_7_8_SE2
case iPhone_6_6S_7_8_PLUS
case iPhone_X_XS_12mini
case iPhone_XR_11
case iPhone_XS_11Pro_Max
case iPhone_12_Pro
case iPhone_12_Pro_Max
}
    

var hasHomeButton: Bool {
switch type {
case . iPhone_X_XS_12mini, . iPhone_XR_11, .iPhone_XS_11Pro_Max, . iPhone_XS_11Pro_Max, .iPhone_12_Pro, .iPhone_12_Pro_Max:
return false
default:
return true
}
}
    

var type: Type {
if UI_USER_INTERFACE_IDIOM() == .phone {
switch UIScreen.main.nativeBounds.height {
case 1136:
return .iPhone_5_5S_5C_SE1
case 1334:
return .iPhone_6_6S_7_8_SE2
case 1920, 2208:
return .iPhone_6_6S_7_8_PLUS
case 2436:
return .iPhone_X_XS_12mini
case 2532:
return .iPhone_12_Pro
case 2688:
return .iPhone_XS_11Pro_Max
case 2778:
return .iPhone_12_Pro_Max
case 1792:
return .iPhone_XR_11
default:
assertionFailure("Unknown phone device detected!")
return .iPhone_6_6S_7_8_SE2
}
} else {
assertionFailure("Unknown idiom device detected!")
return .iPhone_6_6S_7_8_SE2
}
}
}

使用简单的方法检测任何设备。像下面,

func isPhoneDevice() -> Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}


func isDeviceIPad() -> Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}


func isPadProDevice() -> Bool {
let SCREEN_WIDTH: CGFloat = UIScreen.main.bounds.size.width
let SCREEN_HEIGHT: CGFloat = UIScreen.main.bounds.size.height
let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)


return UIDevice.current.userInterfaceIdiom == .pad && SCREEN_MAX_LENGTH == 1366.0
}


func isPhoneXandXSDevice() -> Bool {
let SCREEN_WIDTH = CGFloat(UIScreen.main.bounds.size.width)
let SCREEN_HEIGHT = CGFloat(UIScreen.main.bounds.size.height)
let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)


return UIDevice.current.userInterfaceIdiom == .phone && SCREEN_MAX_LENGTH == 812.0
}


func isPhoneXSMaxandXRDevice() -> Bool {
let SCREEN_WIDTH = CGFloat(UIScreen.main.bounds.size.width)
let SCREEN_HEIGHT = CGFloat(UIScreen.main.bounds.size.height)
let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)


return UIDevice.current.userInterfaceIdiom == .phone && SCREEN_MAX_LENGTH == 896.0
}

像这样叫,

if isPhoneDevice() {
// Your code
}

我认为苹果不希望我们手动检查设备是否有“notch”或“home indicator”,但工作的代码是:

-(BOOL)hasTopNotch{


if (@available(iOS 11.0, *)) {


float max_safe_area_inset = MAX(MAX([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top, [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.right),MAX([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.bottom, [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.left));


return max_safe_area_inset >= 44.0;


}


return  NO;


}


-(BOOL)hasHomeIndicator{


if (@available(iOS 11.0, *)) {


int iNumberSafeInsetsEqualZero = 0;


if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top == 0.0)iNumberSafeInsetsEqualZero++;
if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.right == 0.0)iNumberSafeInsetsEqualZero++;
if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.bottom == 0.0)iNumberSafeInsetsEqualZero++;
if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.left == 0.0)iNumberSafeInsetsEqualZero++;


return iNumberSafeInsetsEqualZero <= 2;


}


return  NO;


}

其他一些帖子都没用。例如,竖屏模式下“通话状态栏”(绿色栏)的iPhone 6S就有一个很大的顶部安全插件。在我的代码中,所有的情况都被占用了(即使设备在纵向或横向启动)

2019年11月:

以下是我在所有生产项目中使用的。注意这个要点很长。

  1. 这并不使用宽度或高度的计算,而是:
  2. 它检查设备字符串模型。
  3. 没有风险让你的构建被苹果拒绝,因为使用任何私人/未记录的api。
  4. 工作与模拟器💯

    import UIKit
    
    
    class DeviceUtility {
    /// Determines if the current device of the user is an iPhoneX type/variant.
    static var isIphoneXType: Bool {
    get {
    switch UIDevice().type {
    case .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhoneX, .iPhone11, .iPhone11Pro, .iPhone11ProMax: return true
    default: return false
    }
    }
    }
    }
    
    
    
    
    public enum DeviceModel : String {
    case simulator     = "simulator/sandbox",
    
    
    // MARK: - iPods
    
    
    iPod1              = "iPod 1",
    iPod2              = "iPod 2",
    iPod3              = "iPod 3",
    iPod4              = "iPod 4",
    iPod5              = "iPod 5",
    
    
    // MARK: - iPads
    
    
    iPad2              = "iPad 2",
    iPad3              = "iPad 3",
    iPad4              = "iPad 4",
    iPadAir            = "iPad Air ",
    iPadAir2           = "iPad Air 2",
    iPad5              = "iPad 5", //aka iPad 2017
    iPad6              = "iPad 6", //aka iPad 2018
    
    
    // MARK: - iPad Minis
    
    
    iPadMini           = "iPad Mini",
    iPadMini2          = "iPad Mini 2",
    iPadMini3          = "iPad Mini 3",
    iPadMini4          = "iPad Mini 4",
    
    
    // MARK: - iPad Pros
    
    
    iPadPro9_7         = "iPad Pro 9.7\"",
    iPadPro10_5        = "iPad Pro 10.5\"",
    iPadPro12_9        = "iPad Pro 12.9\"",
    iPadPro2_12_9      = "iPad Pro 2 12.9\"",
    
    
    // MARK: - iPhones
    
    
    iPhone4            = "iPhone 4",
    iPhone4S           = "iPhone 4S",
    iPhone5            = "iPhone 5",
    iPhone5S           = "iPhone 5S",
    iPhone5C           = "iPhone 5C",
    iPhone6            = "iPhone 6",
    iPhone6plus        = "iPhone 6 Plus",
    iPhone6S           = "iPhone 6S",
    iPhone6Splus       = "iPhone 6S Plus",
    iPhoneSE           = "iPhone SE",
    iPhone7            = "iPhone 7",
    iPhone7plus        = "iPhone 7 Plus",
    iPhone8            = "iPhone 8",
    iPhone8plus        = "iPhone 8 Plus",
    iPhoneX            = "iPhone X",
    iPhoneXS           = "iPhone XS",
    iPhoneXSMax        = "iPhone XS Max",
    iPhoneXR           = "iPhone XR",
    iPhone11           = "iPhone 11",
    iPhone11Pro        = "iPhone 11 Pro",
    iPhone11ProMax     = "iPhone 11 Pro Max",
    
    
    // MARK: - Apple TVs
    
    
    AppleTV            = "Apple TV",
    AppleTV_4K         = "Apple TV 4K",
    
    
    // MARK: - Unrecognized
    
    
    unrecognized       = "?unrecognized?"
    }
    
    
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    //MARK: UIDevice extensions
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    
    
    public extension UIDevice {
    var type: DeviceModel {
    var systemInfo = utsname()
    uname(&systemInfo)
    let modelCode = withUnsafePointer(to: &systemInfo.machine) {
    $0.withMemoryRebound(to: CChar.self, capacity: 1) {
    ptr in String.init(validatingUTF8: ptr)
    
    
    }
    }
    let modelMap : [ String : DeviceModel ] = [
    
    
    // MARK: - Simulators
    
    
    "i386"      : .simulator,
    "x86_64"    : .simulator,
    
    
    // MARK: - iPod
    
    
    "iPod1,1"   : .iPod1,
    "iPod2,1"   : .iPod2,
    "iPod3,1"   : .iPod3,
    "iPod4,1"   : .iPod4,
    "iPod5,1"   : .iPod5,
    
    
    // MARK: - iPad
    
    
    "iPad2,1"   : .iPad2,
    "iPad2,2"   : .iPad2,
    "iPad2,3"   : .iPad2,
    "iPad2,4"   : .iPad2,
    "iPad3,1"   : .iPad3,
    "iPad3,2"   : .iPad3,
    "iPad3,3"   : .iPad3,
    "iPad3,4"   : .iPad4,
    "iPad3,5"   : .iPad4,
    "iPad3,6"   : .iPad4,
    "iPad4,1"   : .iPadAir,
    "iPad4,2"   : .iPadAir,
    "iPad4,3"   : .iPadAir,
    "iPad5,3"   : .iPadAir2,
    "iPad5,4"   : .iPadAir2,
    "iPad6,11"  : .iPad5, //aka iPad 2017
    "iPad6,12"  : .iPad5,
    "iPad7,5"   : .iPad6, //aka iPad 2018
    "iPad7,6"   : .iPad6,
    
    
    // MARK: - iPad mini
    
    
    "iPad2,5"   : .iPadMini,
    "iPad2,6"   : .iPadMini,
    "iPad2,7"   : .iPadMini,
    "iPad4,4"   : .iPadMini2,
    "iPad4,5"   : .iPadMini2,
    "iPad4,6"   : .iPadMini2,
    "iPad4,7"   : .iPadMini3,
    "iPad4,8"   : .iPadMini3,
    "iPad4,9"   : .iPadMini3,
    "iPad5,1"   : .iPadMini4,
    "iPad5,2"   : .iPadMini4,
    
    
    // MARK: - iPad pro
    
    
    "iPad6,3"   : .iPadPro9_7,
    "iPad6,4"   : .iPadPro9_7,
    "iPad7,3"   : .iPadPro10_5,
    "iPad7,4"   : .iPadPro10_5,
    "iPad6,7"   : .iPadPro12_9,
    "iPad6,8"   : .iPadPro12_9,
    "iPad7,1"   : .iPadPro2_12_9,
    "iPad7,2"   : .iPadPro2_12_9,
    
    
    // MARK: - iPhone
    
    
    "iPhone3,1" : .iPhone4,
    "iPhone3,2" : .iPhone4,
    "iPhone3,3" : .iPhone4,
    "iPhone4,1" : .iPhone4S,
    "iPhone5,1" : .iPhone5,
    "iPhone5,2" : .iPhone5,
    "iPhone5,3" : .iPhone5C,
    "iPhone5,4" : .iPhone5C,
    "iPhone6,1" : .iPhone5S,
    "iPhone6,2" : .iPhone5S,
    "iPhone7,1" : .iPhone6plus,
    "iPhone7,2" : .iPhone6,
    "iPhone8,1" : .iPhone6S,
    "iPhone8,2" : .iPhone6Splus,
    "iPhone8,4" : .iPhoneSE,
    "iPhone9,1" : .iPhone7,
    "iPhone9,3" : .iPhone7,
    "iPhone9,2" : .iPhone7plus,
    "iPhone9,4" : .iPhone7plus,
    "iPhone10,1" : .iPhone8,
    "iPhone10,4" : .iPhone8,
    "iPhone10,2" : .iPhone8plus,
    "iPhone10,5" : .iPhone8plus,
    "iPhone10,3" : .iPhoneX,
    "iPhone10,6" : .iPhoneX,
    "iPhone11,2" : .iPhoneXS,
    "iPhone11,4" : .iPhoneXSMax,
    "iPhone11,6" : .iPhoneXSMax,
    "iPhone11,8" : .iPhoneXR,
    "iPhone12,1" : .iPhone11,
    "iPhone12,3" : .iPhone11Pro,
    "iPhone12,5" : .iPhone11ProMax,
    
    
    // MARK: - AppleTV
    
    
    "AppleTV5,3" : .AppleTV,
    "AppleTV6,2" : .AppleTV_4K
    ]
    
    
    if let model = modelMap[String.init(validatingUTF8: modelCode!)!] {
    if model == .simulator {
    if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
    if let simModel = modelMap[String.init(validatingUTF8: simModelCode)!] {
    return simModel
    }
    }
    }
    return model
    }
    return DeviceModel.unrecognized
    }
    }
    

Usage: let inset: CGFloat = DeviceUtility.isIphoneXType ? 50.0 : 40.0

我试图使工作之前的答复,他们都没有为我工作。所以我为SwiftUI找到了一个解决方案。创建一个名为UIDevice+Notch.swift的文件

其内容:

extension UIDevice {
var hasNotch: Bool {
let bottom = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return bottom > 0
}
}

用法:

if UIDevice.current.hasNotch {
//... consider notch
} else {
//... don't have to consider notch
}

下面是Objective-C中需要的两个宏。

#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top > 24.0)

用法:

if (IS_IPHONE_X) {
}


我希望它能帮助别人。

如何检测iOS设备型号和屏幕大小?

CheckDevice检测当前的设备型号和屏幕大小。

你也可以使用

< p > CheckDevice.size () 返回iPhone 12 mini的.screen5_4Inch

< p >等等…… 也许…< / p >

CheckDevice.isPhone ()

检查设备类型为iPhone。

CheckDevice.isWatch ()

CheckDevice.isSimulator ()

CheckDevice.isPad ()

伟大的回购

< p > CheckDevice https://github.com/ugurethemaydin/CheckDevice < / p >