检测设备是否是iPhone X

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

如果某个应用程序在iPhone X上运行,是否有人已经知道如何以编程方式可靠地检测(在Objective-C中)?

编辑:

当然,检查屏幕大小是可能的,但是我不知道是否有像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(例如通过某些SDKfunction)还是必须使用间接测量”

根据迄今为止的答案,我认为答案是“不,没有直接的方法,测量是要走的路”。

根据你的问题答案是否定的,没有直接的方法,更多的信息你可以从here1和here2得到信息

如果你想检测iPhone X的高度使用2436px

设备屏幕大小和方向

在这里输入图像描述

迅速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 2208: print("iPhone 6+/6S+/7+/8+") case 2436: print("iPhone X") default: print("unknown") } } 

目标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 2208: printf("iPhone 6+/6S+/7+/8+"); break; case 2436: printf("iPhone X"); break; default: printf("unknown"); } } 

基于你的问题如下

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

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

欲了解更多信息,你可以在这里

UPDATE

不要使用userInterfaceIdiom属性来识别设备types,正如苹果文档所解释的那样,

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

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

正确的方法是通过uname获取机器名称,请检查此线程的细节。

另一个可能性,目前正在工作,因为iPhone X是唯一一个在顶部缺口的人。 这是我真正在这里检测到的:

  iphoneX = NO; if (@available(iOS 11.0, *)) { if (_window.safeAreaInsets.top > 0.0) { iphoneX = YES; } } 

当然,如果您处于横向方向,则可能需要检查左侧和右侧的安全区域插页。

编辑:_window是AppDelegate的UIWindow,这个检查在应用程序didFinishLaunchingWithOptions中完成。

检查设备型号/机器名称 ,不要直接在代码中使用点/像素数,这是硬代码

 #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> BOOL IsiPhoneX(void) { static BOOL isiPhoneX = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ #if TARGET_IPHONE_SIMULATOR NSString *model = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"]; #else struct utsname systemInfo; uname(&systemInfo); NSString *model = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; #endif isiPhoneX = [model isEqualToString:@"iPhone10,3"] || [model isEqualToString:@"iPhone10,6"]; }); return isiPhoneX; } 

所有这些基于尺寸的答案都容易在未来的设备上出现不正确的行为。 他们今天会工作,但是如果明年有一台iPhone的尺寸相同,但在玻璃下面有相机等,那么如果没有“缺口”呢? 如果唯一的select是更新应用程序,那么这对您和您的客户来说是一个糟糕的解决scheme。

您也可以检查硬件型号string,如“iPhone10,1”,但这是有问题的,因为有时苹果为全球不同的运营商发布不同的型号。

正确的方法是重新devise顶层布局,或者解决您使用自定义导航栏高度时遇到的问题(这就是我所关注的)。 但是,如果你决定不去做这两件事情,那么无论你在做什么都是一件骇人的事情,那么今天就可以开始工作,而且你需要在某个时候纠正它,也许需要多次纠正它,以保持黑客加工。

你可以按照这个尺寸来检测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 } 

在这里输入图像描述

但是

这是不够的方法。 如果苹果公司宣布下一款具有相同尺寸的iPhone X的iPhone,那么最好的办法就是使用硬件string来检测设备。

对于较新的设备硬件string如下。

iPhone 8 – iPhone10,1iPhone 10,3

iPhone 8 Plus – iPhone10,2iPhone 10,4

iPhone X – iPhone10,5iPhone10,6

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

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

请注意,这在模拟器中不起作用,只能在实际的设备上使用。

 #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) #define IS_RETINA ([[UIScreen mainScreen] scale] == 2.0) #define IS_IPAD_DEVICE [(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"] 

根据@ saswanb的回答,这是一个Swift 4版本:

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

您不应该假设苹果公司以不同的UINavigationBar高度发布的唯一设备是iPhone X.尝试使用更通用的解决scheme来解决此问题。 如果您希望栏总是比默认高度大20px,那么代码应该将20px添加到栏的高度,而不是将其设置为64px(44px + 20px)。

SWIFT 4答案

Mehod 1:

通过使用window?.safeAreaInsets.top

  var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) if #available(iOS 11.0, *) { if (window?.safeAreaInsets.top)! > CGFloat(0.0) || window?.safeAreaInsets != .zero { print("iPhone X") } else { print("Not iPhone X") } } return true } 

方法2: 注意:需要真正的设备进行testing

参考

  let deviceType = UIDevice.current.modelName if deviceType.range(of:"iPhone10,3") != nil || deviceType.range(of:"iPhone10,6") != nil { print("iPhone X") } else { print("Not iPhone X") } 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 } } 
 #define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) #define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f) 

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

用于处理顶尖(状态栏,导航栏)或底部回家指示器(tabbar)等。

 class var hasSafeAreaInsets: Bool { if #available(iOS 11.0, tvOS 11.0, *) { return UIApplication.shared.delegate?.window??.safeAreaInsets != .zero } return false } 

背景大小,全屏function等

 class var isIphoneXOrBigger: Bool { return UIScreen.main.bounds.height >= 812 } 

注意:最终将它与UIDevice.current.userInterfaceIdiom == .phone混合
注意:这种方法需要有一个LaunchScreen故事板或正确的LaunchImages

为背景比例,滚动function等

 class var isIphoneXOrLonger: Bool { return UIScreen.main.bounds.height / UIScreen.main.bounds.width >= 812.0 / 375.0 } 

注意:这种方法需要有一个LaunchScreen故事板或正确的LaunchImages

分析,统计,追踪等

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

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

for 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 } 

我知道这只是一个Swift解决scheme,但它可以帮助某人。

我在每个项目中都有globals.swift ,而且我总是在其中添加的一个是DeviceType来快速检测用户的设备:

 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 } 

这可能是由一些其他的StackOverflow答案,因为这是我所有的好东西!

 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 } 
 - (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进行检查,则检查将失败,因为height已用width换出。

这就是为什么我的解决scheme在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 } } 

有几个原因想要知道什么是设备。

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

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

  3. 如果要显示设备名称,例如,将其包含在电子邮件中用于诊断目的,则在使用sysctl ()检索设备模型后,可以使用相当于此名称的名称:

     $ curl http://appledevicenames.com/devices/iPhone10,6 iPhone X