检测设备是否为iPhone X


262

我的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功能),还是必须使用间接测量”

根据到目前为止给出的答案,我认为答案是“不,没有直接的方法。测量是必经之路”。


iPhone X的屏幕分辨率与其他屏幕不同。
El Tomato

2
是的,正如我在编辑中提到的那样,可以检查屏幕尺寸。但是,问题是,是否存在“直接”方法来查询设备类型,而不是“间接”测量值……
Andrei Herford

3
作者只想获取设备类型,而不是屏幕分辨率。为什么不直接检查机器名称? @lubilis是正确的。
Itachi

2
您为什么不只使用Apple推荐的安全区指南?
Holex

4
重要提示,未来的开发者:不要像目前的主流解决方案所建议的那样利用屏幕高度来检测到这种情况,这很不好,因为它可能导致未来设备的误报。如果尚未渲染UIWindow(如在AppDelegate的init函数中),则将不起作用,无法在横向应用程序中工作,并且如果设置了缩放比例,则可能无法在模拟器上运行。切勿对此类事物使用幻数!您可以像在这里一样检查硬件标志以确保成功:stackoverflow.com/a/51511947/2057171
Albert Renshaw

Answers:


383

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

iPhone X高度为2436像素

设备屏幕尺寸和分辨率

在此处输入图片说明

设备屏幕尺寸和方向

在此处输入图片说明

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

目标-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:
            print("iPhone X/XS/11 Pro");
             break;

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

        case 1792:
            print("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

如果有人考虑使用陷波来检测iPhoneX,请注意在“横向”上所有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
}

目标-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设备的屏幕大小不同,因此您可能希望根据当前设备的类型创建不同的视图和控件。

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

正确的方法是通过获得计算机名称uname。检查以下详细信息:


iPhone X的分辨率是根据2436 X 1125像素:iphonesoft.fr/2017/09/12/...
Medhi

1
@Medhi-iPhone X的分辨率为-1125 x 2436像素(
〜458

14
没有!iPhone应用程序(不是Universe)可以通过App Store安装在iPad设备上,在这种情况下,userInterfaceIdiom也会返回UIUserInterfaceIdiomPhone。这个答案是错误的。
Itachi

1
@ThreeCoins,请根据Leo Dabus的建议更新您的答案,以获取正号设备。它适用于Plus模拟器,但不适用于设备。
Hiren Gujarati

2
这很不好,因为它可能导致将来的设备出现误报。如果UIWindow尚未渲染(AppDelegate),则将不起作用,无法在横向应用程序中使用,并且如果设置了比例,则可能无法在模拟器上运行。您可以像在这里一样检查硬件标志以确保成功:stackoverflow.com/a/51511947/2057171
Albert Renshaw

101

另一种可能在iOS 11和iOS 12上运行的可能性是因为iPhone X是唯一一个顶部有缺口,凹痕为44的缺口。这就是我在这里真正检测到的:

目标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
}

当然,如果您横向放置,则可能需要检查左右安全区域插图。

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

答案已针对iOS 12更新,以检查top> 24而不是top> 0。

编辑:在模拟器中,您可以转到硬件,切换通话状态栏。这样做告诉我,通话时状态栏高度在iOS 11的iPhone X或iPhone XS iOS 12上不会改变。在这两种情况下,所有更改都是时间图标,该图标的背景为绿色。这很简单:

在此处输入图片说明


5
安全区域插图将包含状态栏在其他设备上的高度(如果可见)。检查是否为0只会告诉您状态栏是否可见,而不是设备是否为
iPhoneX。– IMcD23

3
库克说:“这可能会破坏iPhone Xs或iPhone 11的性能。”
Itachi

11
我已经适应了一点,使用if _window.safeAreaInsets != UIEdgeInsets.zero允许任何设备的方向
弗雷泽

2
如果您不想使用.topsafeAreaInsets.bottom则在iPhone X上为34,在其他设备上为0
blwinters

7
警告:请勿使用它,它会在iOS 12上中断。在这种情况下,也没有记录UIWindow应该做什么。openradar.appspot.com/42372793
steipete

73

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

用于处理底部主页指示器(选项卡)等。

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
}

我希望return LAContext().biometryType == .typeFaceID即使用户拒绝canEvaluatePolicy也可以使用该方法,但对我不起作用,它仍然会返回.none
Jeremy

@Jeremy,这是有记录的行为,是Apple隐私政策的结果。这就是为什么该方法上方的注释。
心教堂

啊,我误解了你的评论。我以为您的意思是使用canEvaluatePolicy可能会失败,所以请改用以下内容。我发现您被允许检查设备是否具有人脸ID有点奇怪,直到用户响应切换,然后您再也无法检查。我应该如何提供一条有用的错误消息,说去设置并切换人脸ID?
杰里米(Jeremy),

@杰里米(Jeremy)我没有iPhone X,所以不知道。也许您可以使用(model == "iPhone10,3" || model == "iPhone10,6")上方的模型检测,如果canUseFaceID返回false,则表示它被用户拒绝。
心教堂

1
@MateoOlaya我的答案没有被苹果拒绝:您可以全部使用。
心教堂

42

您可以执行此操作以根据尺寸检测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     
}

在此处输入图片说明

但是

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

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

iPhone 8- iPhone10,1iPhone 10,4

iPhone 8 Plus- iPhone10,2iPhone 10,5

iPhone X- iPhone10,3iPhone10,6


2
您应该使用[UIDevice currentDevice]而不是[[UIDevice alloc] init]
S. Matsepura

硬件字符串的唯一问题是它在模拟器上不起作用
凌晨

38

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

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

    return [notchiModels containsObject:GetDeviceModel()];
}

1
出色的答案,因为它可以正确处理模拟器。请将#import行添加到“完整代码”部分。我第一次尝试时就错过了(复制/粘贴)。
mpoisot

1
那是我的首选方法。有关设备模型字符串的完整列表,请参考此Wiki。作为附带说明,@“ iphone10,3”也可以视为硬代码。
YvesLeBorg

1
@YvesLeBorg是的,这确实是一个关键的有争议的问题。我认为,硬件模型字符串比设备的屏幕点具有唯一的标识符。通常,它用于数据统计。
Itachi

25
#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]边界] .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
注意,它仅适用于纵向方向
-CFIFok,

1
谢谢你 效果很好。在横向模式下,您需要调整这些数字。横向模式下iPhoneX的魔数为375.0
pvella

有一些iPhone Plus / Max / Pro nativeScale与一起使用3.0,对吗?
Itachi

24

看完所有答案后,这就是我最终要做的事情:

解决方案(与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开始,您可以使用Target环境平台条件检查应用程序是否在模拟器上运行:

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

(较旧的方法仍然可以使用,但是此新方法更能证明未来的发展)


苹果可以吗?
Surjeet Rajput,

@ commando24是的,由于此代码,我认为他们没有任何理由拒绝该应用程序。
Cloud9999Strife

18

基于尺寸的所有这些答案很容易在将来的设备上出现不正确的行为。它们将在今天工作,但是如果明年有一部尺寸相同的iPhone,但相机在玻璃罩下等,这样就不会有“凹口”了怎么办?如果唯一的选择是更新应用程序,那么这对您和您的客户来说都是一个糟糕的解决方案。

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

正确的方法是重新设计顶部布局,或解决自定义导航栏高度(这就是我要关注的高度)方面的问题。但是,如果您决定不做任何一件事情,请意识到您正在做的事情是使今天的代码正常工作的hack ,并且您需要在某个时间点(可能是多次)进行纠正以保持hacks加工。


1
对。将数字X始终为A的假设提炼为一个数字X始终为A的假设,除非条件Y等于B时才深入探讨。尺寸基于苹果公司指定的安全区域,而不是通过猜测来确定。
汤米

2
我会担心实际上没有下一部iPhone的情况。我希望我的应用今天可以正常工作。
Vahid Amiri

13

SWIFT 4+答案

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

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

参考

 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,您可以删除func之外的“ var window”属性,而删除其中的“ let”常量(类型为UIWindow,即非可选)。我喜欢这个答案,因为在启动时,self.view.window可能为nil,而UIApplication.shared.keyWindow也可能为nil,而以这种方式创建UIWindow每次都会起作用。
Rolleric '17

11

SWIFT 4/5可重复使用的扩展iPhone 11支持

    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_11_Pro_Max
        case iPhone_Xr_11
        case iPhone_11_Pro
    }

    public var hasHomeButton: Bool {
        switch type {
        case .iPhone_X_Xs, .iPhone_Xr_11, .iPhone_Xs_11_Pro_Max, .iPhone_11_Pro:
            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_11_Pro_Max
            case 1792: return .iPhone_Xr_11
            case 2426: return .iPhone_11_Pro
            default: return .iPhone_unknown
        }
        }
        return .iPad
   }
}

2
扩展良好,但最有用的是UIDevice.current.hasHomeButton
WINSergey

1
@ale_stro使用userInterfaceIdiom确定通用应用程序的设备好吗?大多数人不建议这样做。使用它有什么危害吗?
shaqir

10

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

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

请注意,这仅在实际设备上才在Simulator中起作用。


此处的所有设备代码:iphonesoft.fr/2016/10/31/… 示例:iPhone X:iPhone10,5和iPhone10,6
Medhi

维基百科上硬件字符串显示为“ iPhone10,3和iPhone10,6”。@Medhi
Itachi

@Medhi,您可以ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]在Simulator中使用以从Xcode获取实际值。
心教堂

9

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

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

状态栏也被视为在安全区域之外!因此,这将返回误报!它应该高于20点(状态栏的高度)。如果设备是iPhone Xs,R或Xs Max,则返回true。
MQoder

代码的伟大工程,但要注意:keyWindownil,直到主视图控制器呼吁viewDidAppear
凯西

9

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

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.maxWH == 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没有这些图像将无法返回正确的值。


1
刚接触Swift的朋友问如何使用它,以防万一其他人不知道… if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling

5

height由于一个原因,所有使用的答案仅是故事的一半。如果要在设备方向为时进行此类检查,landscapeLeft否则landscapeRight检查将失败,因为会height被替换为width

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

请改用nativeBounds
Leo Dabus,

4

您不应假定Apple发行的唯一具有不同UINavigationBar高度的设备将是iPhoneX。请尝试使用更通用的解决方案来解决此问题。如果您希望栏始终比其默认高度大20px,则您的代码应在栏的高度上添加20px,而不是将其设置为64px(44px + 20px)。


那么,您还必须提出什么其他解决方案?
Stephane Mathis

@xaphod现在有更好的答案。
心教堂

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

4

迅捷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
}

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

2
如果您上传iPhone X的默认图片,它将返回812。直到那时,我认为它将返回您iPhone 7的大小,但是不确定...
Fahim Parkar

3
- (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;
    }
}

1
最佳答案!无需任何设备大小的像素值。
Peter Kreinz

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

然后将这些额外功能添加到顶部或底部约束中


3

对于本机边界高度为2001px而不是2436px的用户(如我),这是因为您是使用iOS 11之前的旧SDK(Xcode 8而不是Xcode 9)构建应用的。使用较旧的SDK,iOS会在iPhone X上将应用程序显示为“黑框”,而不是将屏幕边缘扩展到顶部,而不是顶部的“传感器缺口”。这会减小屏幕尺寸,这就是该属性返回2001而不是2436的原因。

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


3

不要使用其他解决方案所建议的屏幕像素大小,这很不好,因为它可能导致将来的设备出现误报。如果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]);\
})()\
)

我能想到的检测iPhoneX的唯一原因是避免屏幕顶部出现缺口。如果是这样,您可以检查safeArea.top以检测所述缺口的大小。只需确保在UIWindow加载后进行测量即可,所以不是在viewDidLoad期间进行测量,而是在以下一个线程周期进行测量:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw

2

我详细说明了您的其他答案,并在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
    }
}

与其使用Mirrorsysctlbyname不如使用Cloud9999Strife答案(以及我的答案)中的方法更快。
心教堂

2

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

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

这是用于应用程序肖像。您也可以根据设备方向检查尺寸。另外,在其他iPhone上,状态栏可能被隐藏,因此框高为0。在iPhone X上,状态栏永远不会被隐藏。


您可以在隐藏iPhoneX的状态栏。controller与此: - (BOOL)prefersStatusBarHidden { return YES; } 那么状态栏的高度为0
无夜之星辰

@无夜之星辰我在启动时在AppDelegate中进行了检查。
Tiois

2

我当时使用的是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

2

或者,您可以签出“ DeviceKit ”窗格。安装后,只需检查以下设备即可:

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

2

2019年11月:

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

  1. 这不使用宽度或高度的计算,而是:
  2. 它检查设备字符串模型。
  3. 由于使用任何私有/未记录的API,因此没有被Apple拒绝构建的风险。
  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
        }
    }

用法:让inset:CGFloat = DeviceUtility.isIphoneXType?50.0:40.0


完美运作。谢谢。我在SwiftUI项目中使用它。
LondonGuy

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及更早版本上的安全区域插图。

@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

纵向模式下,我使用视图框架的宽度和高度来检查:

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

}

纵向屏幕尺寸在此处列出

在此处输入图片说明


0

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

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

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

  3. 例如,如果要显示设备名称以供诊断之用,请在使用检索设备型号后使用sysctl ()该名称的等效名称来显示该名称:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.