Визначте, чи пристроєм є 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");
}

EDIT 2:

Я не думаю, що моє запитання є дублікатом пов'язаного питання. Звичайно, існують методи "вимірювати" різні властивості поточного пристрою та використовувати результати, щоб вирішити, який пристрій використовувати. Однак це не було фактичним моїм питанням, як я намагався наголосити в першій редакції.

Актуальне питання: "Чи можна безпосередньо виявити, чи поточний пристрій є iPhone X (наприклад, за якоюсь функцією SDK) чи мені потрібно використовувати непрямі вимірювання ?"

Відповідно до наведених відповідей, я припускаю, що відповідь "Ні, прямих методів немає. Вимірювання - це шлях".


У iPhone X роздільна здатність екрана відрізняється від інших.
El Tomato

2
Так, як я вже згадував у своїй редакції, можна перевірити розмір екрана. Однак питання полягає в тому, чи існує "прямий" метод для запиту типу пристрою, а не "непрямі" вимірювання ...
Андрій Герфорд

3
Автор просто хоче отримати тип пристрою, а не роздільну здатність екрана. Чому б не перевірити ім’я машини безпосередньо? @lubilis має рацію.
Ітачі

2
чому ви просто не використовуєте посібники з безпечної зони, як це рекомендує Apple?
holex

4
ВАЖЛИВО, майбутні розробники: не визначайте цю висоту екрану, як підказують поточні верхні рішення, це погано, оскільки це може призвести до помилкових позитивних результатів для майбутніх пристроїв; не працюватиме, якщо UIWindow ще не виведений (як у ваших функціях init AppDelegate), не працюватиме в альбомних додатках і може вийти з ладу на тренажері, якщо встановлено масштаб. НІКОЛИ не використовуйте магічні числа для таких речей! Ви можете перевірити апаратні прапори, щоб гарантувати успіх, як я це зробив тут: stackoverflow.com/a/51511947/2057171
Альберт Реншов,

Відповіді:


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 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 Human Interface:

Швидкий :

Виявити 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 мають різний розмір екрана, тому ви, можливо, захочете створити різні види та елементи керування залежно від типу поточного пристрою.

Тобто ця властивість просто використовується для визначення стилю перегляду запущеного додатка. Однак додаток iPhone (не універсальний) може бути встановлений на пристрої iPad через магазин додатків; у такому випадку userInterfaceIdiomвиклик повернеUIUserInterfaceIdiomPhone .

Правильний спосіб - отримати ім'я машини за допомогою uname. Детальніше про це:


Роздільна здатність iPhone X становить 2436 x 1125 пікселів відповідно до: iphonesoft.fr/2017/09/12/…
Medhi

1
@Medhi - роздільна здатність iphone X становить - 1125 х 2436 пікселів (щільність пікселів
Anbu.Karthik,

14
НЕМАЄ! Додаток iPhone (не всесвіт) може бути встановлено на пристрої iPad через магазин додатків, і в цьому випадку такожuserInterfaceIdiomбуде повернутисьUIUserInterfaceIdiomPhone. Ця відповідь неправильна.
Ітачі

1
@ThreeCoins, оновіть свою відповідь на додаткові пристрої відповідно до пропозиції Лео Дабуса. Він працює на симуляторі Plus, але не на пристрої.
Хірен Гуджараті

2
Це погано, оскільки це може призвести до помилкових позитивних результатів для майбутніх пристроїв; не працюватиме, якщо UIWindow ще не виведено (AppDelegate), не працюватиме в альбомних додатках і може вийти з ладу на тренажері, якщо встановлено масштаб. Ви можете перевірити апаратні прапори, щоб гарантувати успіх, як я це зробив тут: stackoverflow.com/a/51511947/2057171
Альберт Реншоу,

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 - це UIWindow AppDelegate, де ця перевірка робиться в додатку didFinishLaunchingWithOptions.

Відповідь оновлено для iOS 12, щоб перевірити, чи топ> 24, а не топ> 0.

Редагувати: У тренажері ви можете перейти до Обладнання, Переключити рядок стану дзвінка. Це показує мені, що висота рядка стану не змінюється в iPhone X на iOS 11 або iPhone XS iOS 12 під час дзвінка. Все, що змінюється - це значок часу, який отримує зелений фон, в обох випадках. Ось оснастка:

введіть тут опис зображення


5
Вставки безпечної області будуть містити висоту смуги стану, якщо її видно, на інших пристроях. Якщо перевірити, чи це 0, вам підкаже лише те, чи видно рядок стану, а не пристрій iPhone X.
IMcD23,

3
"Це може зламатися в iPhone Xs або iPhone 11 ", - сказав Кук.
Ітачі

11
Я трохи адаптувався і використовую, if _window.safeAreaInsets != UIEdgeInsets.zeroщоб дозволити будь-яку орієнтацію пристрою
Fraser

2
Якщо ви не хочете користуватися .top, safeAreaInsets.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, це документоване поведінка, наслідок політики конфіденційності Apple. Ось чому коментар вище методу.
Cœur

Ах, я неправильно трактував ваш коментар. Я думав, що ви мали на увазі, що використання canEvaluatePolicy може не вдатися, тому скористайтеся наступним. Я вважаю трохи дивним, що вам дозволяється перевірити, чи пристрій має Face ID, поки користувач не відповість на перемикання, і тоді ви навіть не можете перевірити більше. Як я повинен надати корисне повідомлення про помилку, щоб перейти до "Налаштування" та змінити ідентифікатор обличчя?
Джеремі

@Jeremy У мене немає iPhone X, тому я не знаю. Можливо, ви могли б використовувати модель виявлення вище ( model == "iPhone10,3" || model == "iPhone10,6"), а якщо canUseFaceIDповертає помилку, то це означає, що користувач її відхилив.
Cœur

1
@MateoOlaya Ніщо в моїй відповіді Apple не відкине: ви можете використовувати це все.
Cœur

42

Ви можете зробити так, щоб виявити пристрій iPhone X відповідно до розміру.

Швидкий

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

Мета - С

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

введіть тут опис зображення

Але ,

Це недостатній спосіб. Що робити, якщо Apple оголосила наступний iPhone з таким же розміром iPhone X., тож найкращим способом є використання апаратного рядка для виявлення пристрою.

Для новіших пристроїв рядки апаратного забезпечення наведені нижче.

iPhone 8 - iPhone10,1 або iPhone 10,4

iPhone 8 Plus - iPhone10,2 або iPhone 10,5

iPhone X - iPhone10,3 або iPhone10,6


2
Ви повинні використовувати [UIDevice currentDevice]замість[[UIDevice alloc] init]
С. Мацепура

Єдина проблема з апаратним рядком - це те, що він не працює на тренажері
шалено

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
це мій кращий метод. Повний список рядків моделі пристрою див. У цій вікі . Як побічний коментар, @ "iphone10,3" також можна розглядати як жорсткий код.
YvesLeBorg

1
@YvesLeBorg Так, це справді критична суперечлива проблема. Я думаю, що в рядку апаратної моделі є унікальний ідентифікатор, ніж точки екрану для пристрою. Взагалі використовується для статистики даних.
Ітачі

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
Будьте обережні, це чудово працює лише для портретної орієнтації
CFIFо

1
Дякую за це Добре працює. У ландшафтному режимі потрібно відрегулювати ці цифри. Чарівне число iPhoneX в ландшафтному режимі - 375,0
pvella

Є кілька iPhone Plus / Max / Pro, які використовуються nativeScaleз 3.0, правда?
Ітачі

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
}

Примітка

Pre Swift 4.1 ви можете перевірити, чи працює програма на тренажері так:

TARGET_OS_SIMULATOR != 0

З Swift 4.1 і далі ви можете перевірити, чи додаток працює на тренажері, використовуючи умову платформи навколишнього середовища :

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

(старіший метод все ще буде працювати, але цей новий метод є більш надійним доказом)


яблуко буде добре з цим?
Surjeet Rajput

@ commandndo24 Так, я не бачу жодної причини, щоб вони відхиляли додаток через цей код.
Cloud9999Strife

18

Усі ці відповіді на основі розмірів чутливі до неправильної поведінки на майбутніх пристроях. Вони працюватимуть сьогодні, але що робити, якщо наступного року буде iPhone такого ж розміру, але під камерою є камера тощо, щоб не було "висічок"? Якщо єдиний варіант - оновити додаток, то це погане рішення для вас та ваших клієнтів.

Ви також можете перевірити рядок моделі апаратури типу "iPhone10,1", але це проблематично, оскільки іноді Apple випускає різні номери моделей для різних операторів світу.

Правильний підхід полягає в тому, щоб переробити верхній макет або вирішити проблеми, які виникають із власною висотою панелі навігації (саме на цьому я б зосередився). Але якщо ви вирішите не робити жодної з цих речей, зрозумійте, що все, що ви робите, - це злом, щоб сьогодні це працювати , і вам потрібно буде виправити це в якийсь момент, можливо, кілька разів, щоб зберегти хаки робочий.


1
Правильно. Якщо уточнити припущення, що число X завжди буде A до одного, то число X завжди буде A, якщо тільки умова Y, коли воно буде B, просто копає глибше. Розмір, заснований на безпечній зоні, запропонованій Apple, а не за другим відгадуванням.
Томмі

2
Я буду хвилюватися про наступний iPhone, коли він фактично там. Я хочу, щоб моя програма сьогодні працювала.
Вахід Амірі

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 ви можете забрати властивість "var window" поза функцією і просто "нехай" константу всередині нього (введіть UIWindow, тобто не є необов'язковим). Мені подобається ця відповідь, оскільки при запуску self.view.window може бути нульовим, а UIApplication.shared.keyWindow може також бути нульовим, тоді як створення UIWindow таким чином працює щоразу.
Rolleric

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 для визначення пристроїв для універсального додатка? більшість людей цього не рекомендує. чи є якась шкода його використовувати?
Шакір відвідав

10

Так, можливо. Завантажте UIDevice-Hardware (або встановіть через CocoaPod 'UIDevice-Hardware') і використовуйте:

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

Зауважте, що це не буде працювати в Симуляторі, лише на фактичному пристрої.


Весь код пристрою тут: iphonesoft.fr/2016/10/31/… Приклад: iPhone X: iPhone10,5 та iPhone10,6
Medhi

У рядках апаратного забезпечення з wikipedia написано "iPhone10,3 та iPhone10,6". @Medhi
Ітачі

@Medhi, ви можете використовувати ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]в Simulator, щоб отримати фактичні значення з Xcode.
Cœur

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.
MQoder

код працює, але будьте обережні: keyWindowце nilпоки головний контролер уявлення не назвавviewDidAppear
Casey

9

Я знаю, що це лише Швидкий рішення , але воно може комусь допомогти.

Я маю 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
Друг, знайомий у Свіфта, запитав, як це використати, на випадок, якщо хтось ще не знає… if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Ліам Боллінг

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
Лев Дабус,

4

Не слід вважати, що єдиним пристроєм, який Apple випускає з іншою висотою UINavigationBar, буде iPhone X. Спробуйте вирішити цю проблему за допомогою більш загального рішення. Якщо ви хочете, щоб смужка завжди була на 20 пікселів більше, ніж її висота за замовчуванням, ваш код повинен додати 20 пікселів на висоту смуги, а не встановлювати її на 64 пікселів (44 пікселів + 20 пікселів).


Отже, яке ще рішення ви маєте запропонувати?
Стефан Матіс

@xaphod є кращі відповіді зараз.
Cœur

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
він поверне вам 812, якщо ви завантажите зображення за замовчуванням для iPhone X. До цього часу, я думаю, він поверне вам розмір 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
Найкраща відповідь! Без необхідності будь-якого значення розміру пікселя розміру пристрою.
Пітер Крейнц

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 для висоти рідних меж (як я), це тому, що ви створили свій додаток зі старшим SDK, до iOS 11 (Xcode 8 замість Xcode 9). За допомогою старшого SDK iOS відображатиме додатки «чорним ящиком» на iPhone X замість того, щоб розширювати екран від краю до краю, за межею верхнього «датчика». Це зменшує розмір екрана, тому ця властивість повертає 2001 замість 2436.

Найпростіше рішення - просто перевірити обидва розміри, якщо вас цікавить лише виявлення пристрою. Я використовував цей метод для виявлення FaceID під час побудови зі старшим SDK Xcode, який не має значення ENUM, що вказує біометричний тип. У цій ситуації виявлення пристрою за допомогою висоти екрану здавалося найкращим способом дізнатися, чи має пристрій FaceID проти TouchID, не потребуючи оновлення Xcode.


3

НЕ використовуйте розмір пікселя екрана, як пропонують інші рішення, це погано, оскільки це може призвести до помилкових позитивів для майбутніх пристроїв; не працюватиме, якщо UIWindow ще не виведено (AppDelegate), не працюватиме в альбомних додатках і може вийти з ладу на тренажері, якщо встановлено масштаб.

Натомість я зробив для цього макрос, він дуже простий у використанні та покладається на апаратні прапори, щоб запобігти вищезгаданим проблемам.

Редагувати: оновлено для підтримки iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Макс


Використовувати:

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 }
Альберт Реншо

2

Я детально розробив ваші відповіді на чужі відповіді та зробив швидке розширення на UIDevice. Мені подобаються швидкі перерахунки та "все в порядку" та розпорошене. Я створив рішення, яке працює як на пристрої, так і на тренажері.

Переваги: ​​- простий інтерфейс, використання, наприклад UIDevice.current.isIPhoneX -UIDeviceModelType enum дозволяє легко розширювати конкретні функції та константи моделі, які ви хочете використовувати у вашому додатку, наприклад 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
    }
}

Замість використання Mirror, це буде швидше використовувати, sysctlbynameяк це було зроблено у відповіді Cloud9999Strife (і в моїй відповіді теж).
Cœur

2

Я покладаюся на висоту рамки статусу, щоб виявити, чи це iPhone X:

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

Це для застосування без портрета. Ви також можете перевірити розмір відповідно до орієнтації пристрою. Також на інших айфонах смужка стану може бути прихованою, тому висота кадру становить 0. На iPhone X рядок стану ніколи не приховується.


Ви можете приховати статус iPhoneXBar за controllerдопомогою цього: - (BOOL)prefersStatusBarHidden { return YES; } тоді висота statusBar дорівнює 0.
无 夜 之 星辰

@ 无 夜 之 星辰 Я перевіряю це під час завантаження в AppDelegate.
Tiois

2

Я використовував код Пітера Крейнца (тому що він був чистим і робив все, що мені потрібно), але потім зрозумів, що він працює саме тоді, коли пристрій стоїть на портреті (оскільки, очевидно, верхня накладка буде зверху). Тому я створив розширення, щоб обробляти всі орієнтації з відповідними прокладками, без відновлення розміру екрана:

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:

Ось що я використовую у всіх своїх виробничих проектах. Зауважимо, що ця суть досить довга.

  1. Тут не використовуються обчислення ширини чи висоти, а:
  2. Він перевіряє модель рядка пристрою.
  3. Не ризикує Apple відхилити вашу збірку через використання будь-яких приватних / недокументованих API.
  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
        }
    }

Використання: нехай вводиться: 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.