首页 文章

检测设备是否为iPhone X.

提问于
浏览
222

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

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

EDIT:

当然可以检查屏幕的大小,但是,我想知道是否有像 TARGET_OS_IPHONE 这样的"build in"方法来检测iOS ...

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

EDIT 2:

我不认为,我的问题是链接问题的重复 . 当然,有一些方法可以“测量”当前设备的不同属性,并使用结果来决定使用哪个设备 . 然而,这不是我的问题的实际问题,因为我在第一次编辑时试图强调 .

实际问题是: "Is it possible to directly detect if the current device is an iPhone X (e.g. by some SDK feature) or do I have to use indirect measurements"

到目前为止给出的答案,我认为答案是“不,没有直接的方法 . 测量是要走的路” .

30 回答

  • 57

    根据你的问题,答案是否定的 . 没有直接的方法 . 有关更多信息,您可以在此处获取信息:

    iPhone X的高度为2436像素

    来自Device Screen Sizes and resolutions

    enter image description here

    来自Device Screen Sizes and Orientations

    enter image description here

    Swift 3 and later

    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")
    
            case 2688:
                print("iPhone XS Max")
    
            case 1792:
                print("iPhone XR")
    
            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, 2208:
                printf("iPhone 6+/6S+/7+/8+");
                break;
    
            case 2436:
                printf("iPhone X, XS");
                break;
    
            case 2688:
                printf("iPhone XS Max");
                break;
    
            case 1792:
                printf("iPhone XR");
                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");
        } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2688) {
            Console.WriteLine("iPhone XS Max");
        } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1792) {
            Console.WriteLine("iPhone XR");
        } else {
            Console.WriteLine("Unknown");
        }
    }
    

    根据您的问题如下:

    或者使用 screenSize.height 作为float 812.0f not 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人机界面指南中的以下页面:

    Swift

    检测 topNotch

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

    Objective-C

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

    UPDATE

    不要使用 userInterfaceIdiom 属性来标识设备类型,因为documentation for userInterfaceIdiom解释说:

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

    也就是说,此属性仅用于标识正在运行的应用程序的视图样式 . 但是,iPhone应用程序(不是通用)可以通过App Store安装在iPad设备上,在这种情况下, userInterfaceIdiom 也会返回 UIUserInterfaceIdiomPhone .

    正确的方法是通过 uname 获取机器名称 . 请查看以下详细信息:

  • 4

    另一种可能性,适用于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;
            }
        }
    

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

    当然,如果您处于横向方向,则可能需要检查左右安全区域插图 .

    编辑:_window是AppDelegate的UIWindow,其中此检查在应用程序didFinishLaunchingWithOptions中完成 .

    Answer updated for iOS 12 to check if top > 24 rather than top > 0.

    编辑:在模拟器中,您可以转到硬件,切换通话状态栏 . 这样做表明我在进行通话时iOS 11或iPhone XS iOS 12上的iPhone X上的状态栏高度没有变化 . 所有这些变化都是时间图标,在两种情况下都会获得绿色背景 . 这是一个快照:

    enter image description here

  • 17

    您应根据实际需要对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
    }
    

    用于处理底部主页指示符(tabbar)等 .

    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故事板或正确的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)
        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
    }
    
  • 2

    您可以这样做以根据维度检测 iPhone X 设备 .

    Swift

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

    Objective - C

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

    enter image description here

    But

    这还不够 . 如果Apple宣布下一代iPhone具有相同尺寸的iPhone X,那么最好的方法是使用硬件字符串来检测设备 .

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

    iPhone 8 - iPhone10,1iPhone 10,4

    iPhone 8 Plus - iPhone10,2iPhone 10,5

    iPhone X - iPhone10,3iPhone10,6

  • 0

    查看the device model / machine name,不要直接使用代码中的点/像素数,它是hard code并且对于设备硬件没有意义 .

    #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)
    

    请参阅this answer .

    Full code implementation:

    #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)
    {
        NSString *strModelID = GetDeviceModel();
    
        return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"] || // iPhone X
               [strModelID isEqualToString:@"iPhone11,2"] || [strModelID isEqualToString:@"iPhone11,4"] || [strModelID isEqualToString:@"iPhone11,6"] || // iPhone XS (Max)
               [strModelID isEqualToString:@"iPhone11,8"]; // iPhone XR
    }
    
  • 36
    #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_IPHONE_X(IS_IPHONE && [[UIScreen mainScreen] bounds] .size.height == 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"]
    

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

  • 2

    在查看了所有答案后,这就是我最终做的事情:

    解决方案(兼容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
    }
    

    注意

    Pre Swift 4.1您可以检查应用程序是否在模拟器上运行,如下所示:

    TARGET_OS_SIMULATOR != 0
    

    从Swift 4.1开始,您可以使用Target environment platform condition检查应用程序是否在模拟器上运行:

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

    (旧的方法仍然有效,但这种新方法更具前瞻性)

  • 3

    所有这些基于维度的答案都容易受到未来设备上的错误行为的影响 . 他们今天会工作,但是如果明年有一部iPhone的尺寸相同,但玻璃下面有相机等,那么就没有“缺口”了 . 如果唯一的选择是更新应用程序,那么对您和您的客户来说,这是一个糟糕的解决方案 .

    您还可以检查硬件模型字符串,如“iPhone10,1”,但这有问题,因为有时Apple会为全球不同的运营商发布不同的型号 .

    正确的方法是重新设计顶层布局,或者解决你所做的事情,这样做是为了让它工作 today ,你需要在某些时候纠正它,也许多次,以保持黑客工作 .

  • 10

    SWIFT 4+ Answer

    iPhone X, XR, XS, XSMAX:

    注意:需要真正的设备进行测试

    Reference

    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")
            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
        }
    }
    
  • 4

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

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

    请注意,这在模拟器中不起作用,仅在实际设备上有效 .

  • 23

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

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

    我知道这只是一个解决方案,但它可以帮助某人 .

    我在每个项目中都有 globals.swift ,我总是添加的一件事是 DeviceType ,以便轻松检测用户的设备:

    struct ScreenSize {
      static let width = UIScreen.main.bounds.size.width
      static let height = UIScreen.main.bounds.size.height
      static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
      static let maxWH = max(ScreenSize.width, ScreenSize.height)
    }
    
    struct DeviceType {
      static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH < 568.0
      static let iPhone5orSE   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 568.0
      static let iPhone678     = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 667.0
      static let iPhone678p    = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 736.0
      static let iPhoneX       = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 812.0
      static let iPhoneXRMax   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 896.0
      static var hasNotch: Bool {
        return iPhoneX || iPhoneXRMax
      }
    }
    

    然后使用它:

    if DeviceType.hasNotch {
      print("This executes on all phones with a notch")
    }
    
    if DeviceType.iPhone678 {
      print("This executes on iPhones 6, 7 and 8")
    }
    

    如果在项目中使用 LaunchImage ,请确保为所有支持的设备(如XS Max,XR)添加图像,因为如果没有这些设备, UIScreen.main.bounds 将无法返回正确的值 .

  • 4

    SWIFT 4 可重复使用 extension

    public extension UIDevice {
    
        public enum `Type` {
            case iPad
            case iPhone_unknown
            case iPhone_5_5S_5C
            case iPhone_6_6S_7_8
            case iPhone_6_6S_7_8_PLUS
            case iPhone_X_Xs
            case iPhone_Xs_Max
            case iPhone_Xr
        }
    
        public var hasHomeButton: Bool {
            switch type {
            case .iPhone_X_Xs, .iPhone_Xr, .iPhone_Xs_Max:
                return false
            default:
                return true
            }
        }
    
        public var type: Type {
            if userInterfaceIdiom == .phone {
                switch UIScreen.main.nativeBounds.height {
                case 1136:
                    return .iPhone_5_5S_5C
                case 1334:
                    return .iPhone_6_6S_7_8
                case 1920, 2208:
                    return .iPhone_6_6S_7_8_PLUS
                case 2436:
                    return .iPhone_X_Xs
                case 2688:
                    return .iPhone_Xs_Max
                case 1792:
                    return .iPhone_Xr
                default:
                    return .iPhone_unknown
                }
            }
            return .iPad
       }
    }
    
  • 6

    由于一个原因,使用 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
        }
    }
    
  • 3

    您不应该假设Apple发布的唯一具有不同UINavigationBar高度的设备将是iPhone X.尝试使用更通用的解决方案来解决此问题 . 如果您希望条形图总是比其默认高度大20px,则代码应该向条形图的高度添加20px,而不是将其设置为64px(44px 20px) .

  • 25
    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
    }
    
  • 5

    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
    }
    
  • 2
    #define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    #define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
    
  • 93
    - (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;
        }
    }
    
  • 3

    通常,程序员需要将其约束到顶部或底部,因此这些方法可以提供帮助

    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相应

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

  • 41

    对于那些获得2001px而不是2436px的本地边界高度(像我一样),这是因为你在iOS 11(Xcode 8而不是Xcode 9)之前使用较旧的SDK构建了你的应用程序 . 使用较旧的SDK,iOS将在iPhone X上显示“黑盒子”应用,而不是将屏幕边缘到边缘延伸,超出顶部“传感器缺口” . 这会减小屏幕尺寸,这就是该属性返回2001而不是2436的原因 .

    最简单的解决方案是,如果您只对设备检测感兴趣,只需检查两种尺寸 . 我使用这种方法检测FaceID,同时使用较旧的Xcode SDK构建,该SDK没有指定生物识别类型的ENUM值 . 在这种情况下,使用屏幕高度的设备检测似乎是了解设备是否具有FaceID与TouchID而不必更新Xcode的最佳方式 .

  • 7

    一世详细阐述了你对其他人的回答,并在UIDevice上迅速扩展 . 我喜欢快速的枚举和“一切有序”和雾化 . 我已经创建了适用于设备和模拟器的解决方案 .

    优点: - 简单的界面,用途,例如 UIDevice.current.isIPhoneX - UIDeviceModelType 枚举使您能够轻松扩展要在应用中使用的模型特定功能和常量,例如: cornerRadius

    缺点: - 它是特定于模型的解决方案,而不是特定于分辨率的解决方案 - 例如如果Apple将 生产环境 具有相同规格的另一个型号,这将无法正常工作,您需要添加另一个型号才能使这项工作=>您需要更新您的应用程序 .

    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
        }
    }
    
  • 3

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

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

    这是申请人像 . 您还可以根据设备方向检查尺寸 . 此外,在其他iPhone上,状态栏可能是隐藏的,因此帧高度为 0 . 在iPhone X上,状态栏永远不会隐藏 .

  • 7

    我正在使用Peter Kreinz's code(因为它很干净并且做了我需要的东西)但后来我意识到它适用于设备是在肖像上(因为顶部填充将在顶部,显然)所以我创建了一个扩展来处理所有的方向与各自的填充,无需中继屏幕尺寸:

    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
    
  • 311

    不要像其他解决方案所建议的那样使用屏幕像素大小,这很糟糕,因为它可能导致未来设备出现误报;如果UIWindow尚未呈现(AppDelegate),则无法在横向应用程序中运行,并且如果设置了比例,则可能在模拟器上失败 .

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

    编辑:更新以支持iPhoneX,iPhone XS,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]);\
    })()\
    )
    
  • 1

    我最近不得不解决同样的问题 . 虽然这个问题得到了明确的回答(“否”),但这可能有助于其他需要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及更早版本的安全区域insets .

    @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
            }
        }
    }
    
  • 1

    或者,您可以查看'DeviceKit'pod . 安装完成后,您需要做的就是检查设备:

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

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

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

    • 出于布局目的,您还可以使用 UIView.safeAreaInsets .

    • 如果要显示设备名称(例如,要包含在电子邮件中以进行诊断),在使用 sysctl () 检索设备模型后,可以使用等效的名称来表示名称:

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

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

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

    检测设备是否为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" or "iPhone10,6" .

相关问题