检测设备是否是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,1或iPhone 10,3
iPhone 8 Plus – iPhone10,2或iPhone 10,4
iPhone X – iPhone10,5或iPhone10,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
的答案只是一个原因的一半。 如果您要在设备方向为landscapeLeft
或landscapeRight
进行检查,则检查将失败,因为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 } }
有几个原因想要知道什么是设备。
-
您可以检查设备的高度(和宽度)。 这对于布局非常有用,但是如果您想知道确切的设备,通常不会这么做。
-
出于布局的目的,你也可以使用
UIView.safeAreaInsets
。 -
如果要显示设备名称,例如,将其包含在电子邮件中用于诊断目的,则在使用
sysctl ()
检索设备模型后,可以使用相当于此名称的名称:$ curl http://appledevicenames.com/devices/iPhone10,6 iPhone X