iOS機型 iPhone X/XS/XR 判斷的5種方式總結

目前已發佈的所有 iPhone 設備的屏幕數據,包括了最新上市的 iPhone XS、iPhone XS Max 和 iPhone XR,請參考《iPhone 屏幕分辨率終極指南》

最後我們介紹了一種在代碼中通過獲取屏幕的高度判斷是否等於 812.0896.0 來檢測設備是否爲 iPhone X 的方法,但該方法存在小瑕疵,需要考慮一下兩點:

  1. 當 App 支持橫豎屏切換時,在橫屏模式下也能夠正確判斷;
  2. 在模擬器中調試時,能夠正確判斷當前所選則的模擬器類型是不是 iPhone X;

因此,本條小集重新整理一下我們目前所瞭解到的幾種檢測設備是否爲 iPhone X 的方式,供大家參考,不足之處歡迎補充。

  1. 通過獲取設備的 device model 來判斷
  2. 通過獲取屏幕的寬高來判斷
  3. 通過底部安全區域的高度來判斷
  4. 通過是否支持 FaceID 判斷
  5. 通過 UIStatusBar 的高度判斷

備註:這裏所說的 iPhone X 泛指屏幕大小爲 5.8、6.1、6.5 英寸三種尺寸的 iPhone 設備。

在沒有適配的情況下,新設備都是以放大模式自動適配的(以 5.8 寸的 iPhone X 屏幕爲基準等比例放大),此時在代碼中獲取到的屏幕寬高都爲 375pt * 812pt,你需要在啓動圖 LaunchImage 添加對應分辨率的圖片就能獲取到真正的 414pt * 896pt 高度。

方式一:通過獲取設備的 device model 來判斷

每一臺 iOS 設備都有對應的硬件編碼/標識符,稱爲 device model 或者叫 machine name,我們可以通過如下兩種方法來獲取 device model/machine name。

#import <sys/sysctl.h>
#import <sys/utsname.h>

// 獲取 device model/machine name 的方法一
+ (NSString *)machineName1 {
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char *machine = (char *)malloc(size);
    if (machine == NULL) {
        return nil;
    }
    sysctlbyname("hw.machine", machine, &size, NULL, 0);
    NSString *platform = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
    free(machine);
    return platform;
}

// 獲取 device model/machine name 的方法二
+ (NSString *)machineName2 {
    struct utsname systemInfo;
    uname(&systemInfo);
    return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
}

例如,去年發佈的第一代 iPhone X 對應的 device mode 爲 iPhone10,3iPhone10,6,而今年最新發布 iPhone XS 對應 iPhone11,2,iPhone XS Max 對應 iPhone11,4iPhone11,6,iPhone XR 對應 iPhone11,8,完整的 device mode 數據參考這裏:

https://www.theiphonewiki.com/wiki/Models
不過需要注意的是,上述兩種獲取 device model 的方法在模擬器中運行得到的值爲 i386 或 x86_64,因此在模擬器中我們可以通過如下方式正確獲取模擬器所對應的 device model:

// 獲取模擬器所對應的 device model
NSString *model = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];

綜上,我們可以通過判斷 device model 是否爲 “iPhone10,3” 和 “iPhone10,6” 或者以 “iPhone11,”(新設備)開頭,來檢測設備是否爲 iPhone X,完整代碼如下:

+ (BOOL)isiPhoneX {
    static BOOL isiPhoneX = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
#if TARGET_IPHONE_SIMULATOR
        // 獲取模擬器所對應的 device model
        NSString *model = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
#else
        // 獲取真機設備的 device model
        struct utsname systemInfo;
        uname(&systemInfo);
        NSString *model = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
#endif
        // 判斷 device model 是否爲 "iPhone10,3" 和 "iPhone10,6" 或者以 "iPhone11," 開頭
        // 如果是,就認爲是 iPhone X
        isiPhoneX = [model isEqualToString:@"iPhone10,3"] || [model isEqualToString:@"iPhone10,6"] || [model hasPrefix:@"iPhone11,"];
    });
    
    return isiPhoneX;
}

方式二:通過獲取屏幕的寬高來判斷

正如我們前一條小集講到,目前 iPhone X 設備的屏幕寬高對應的開發尺寸只有兩種,分別爲 375pt * 812pt414pt * 896pt,因此我們可以根據屏幕的高度來判斷設備是否爲 iPhone X。但是此時需要考慮設備處於橫屏或者豎屏的情況,這兩種情況的寬高剛好是相反的(當然,如果你的 App 不用支持橫屏的情況,就相對比較簡單了)。

UIDevice 中提供了一個 orientation 屬性用於獲取設備的方向(橫向、豎向、或者水平),一開始我們想着先通過這個屬性判斷設備處於橫屏或者豎屏,然後分別取其對應的屏幕寬度(橫屏下)或者高度(豎屏下)來判斷,但是當這個屬性的值爲 FaceUp 或者 FaceDown(即設備放在水平面上),我們是無法知道此時設備是處於橫屏還是豎屏的。

後面我們想了一個簡便的方法,即獲取屏幕的寬度和高度,取較大一方進行比較是等於 812.0896.0,代碼如下:

+ (BOOL)isiPhoneX {
    // 先判斷當前設備是否爲 iPhone 或 iPod touch
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        // 獲取屏幕的寬度和高度,取較大一方判斷是否爲 812.0 或 896.0
        CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
        CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
        CGFloat maxLength = screenWidth > screenHeight ? screenWidth : screenHeight;
        if (maxLength == 812.0f || maxLength == 896.0f) {
            return YES;
        }
    }
    return NO;
}

方式三:通過底部安全區域的高度來判斷

在去年 iPhone X 發佈後,爲了適配頂部的瀏覽和底部的操作條,蘋果在 iOS 11 上引入安全區域概念,建議開發者在安全區域內進行 UI 佈局,因此我們可以獲取屏幕 keyWindow 的 safeAreaInsets 值來判斷設備是否 iPhone X。

iPhone X 在豎屏下,keyWindow 的 safeAreaInsets 值爲:

{top: 44, left: 0, bottom: 34, right: 0}

而在橫屏下,其值爲:

{top: 0, left: 44, bottom: 21, right: 44}

因此,我們可以比較 safeAreaInsetsbottom 是否等於 34.0 或者 21.0 來判斷設備是否爲 iPhone X,因爲其他設備對應的 bottom 橫豎屏下都爲 0,代碼如下:

+ (BOOL)isiPhoneX {
    if (@available(iOS 11.0, *)) {
        UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
        // 獲取底部安全區域高度,iPhone X 豎屏下爲 34.0,橫屏下爲 21.0,其他類型設備都爲 0
        CGFloat bottomSafeInset = keyWindow.safeAreaInsets.bottom;
        if (bottomSafeInset == 34.0f || bottomSafeInset == 21.0f) {
            return YES;
        }
    }
    return NO;
}

不過該方式有個不足是,必須在 AppDelegate 的 didFinishLaunchingWithOptions 回調中等 keyWindow 初始化之後才能正確判斷。

方式四:通過是否支持 FaceID 判斷

由於目前只有 iPhone X 設備支持 FaceID,因此我們也可以通過判斷設備是否支持 FaceID 來判斷,代碼如下:

+ (BOOL)canUseFaceID {
    if (@available(iOS 11.0, *)) {
        // will fail if user denies `canEvaluatePolicy:error:`
        LAContext *context = [[LAContext alloc] init];
        if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
            return (context.biometryType == LABiometryTypeFaceID);
        }
    }
    return NO;
}

不足:如果用戶禁用 canEvaluatePolicy:error: 方法的使用將無法正確判斷,而且在也不適用於模擬器中的判斷。

方式五:通過 UIStatusBar 的高度判斷

在 iPhone X 之前,所有 iPhone 設備的 StatusBar(狀態欄)高度都爲 20pt,而 iPhone X 的爲 44pt,因此我們可以通過獲取狀態欄的高度判斷是否等於 44.0 來檢測設備是否爲 iPhone X,代碼如下:

+ (BOOL)isiPhoneX {
    CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
    if (statusBarFrame.size.height == 44.0f) {
        return YES;
    }
    return NO;
}

不足:該方法只適用於豎屏且顯示狀態欄的情況下才能正確檢測,而在橫屏模式下,或者 App 隱藏導航欄時,獲取到的狀態欄高度都爲 0statusBarFrame 的值爲 CGRectZero),就無法判斷了。

如果有其他判斷方式,歡迎補充~

結語

最後,絕大部分場景,我們需要檢測設備是否爲 iPhone X 是爲了適配頂部的劉海區域和底部的操作條區域,更推薦通過 Auto Layout 結合 Safe Area 進行 UI 佈局,以適應越來越複雜的屏幕狀況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章