it-swarm.com.ru

Определить, является ли устройство iPhone X

В моем iOS-приложении для UINavigationBar используется настраиваемая высота, что приводит к некоторым проблемам на новом iPhone X. 

Кто-нибудь уже знает, как надежно обнаружить программно (в Objective-C), если приложение работает на iPhone X?

Правка:

Конечно, проверка размера экрана возможна, однако мне интересно, есть ли какой-нибудь метод "build in", такой как 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), или мне нужно использовать косвенные измерения» ?

Исходя из приведенных ответов, я предполагаю, что ответ «Нет, прямых методов нет. Измерения - это путь». 

216
Andrei Herford

На основании вашего вопроса ответ - нет. Прямых методов нет. Для получения дополнительной информации вы можете получить информацию здесь:

а также

Высота iPhone X составляет 2436 пикселей

От Размеры и разрешения экрана устройства :

 enter image description here

От Размеры и ориентация экрана устройства :

 enter image description here

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

        case 2688:
            print("iPhone XS Max")

        case 1792:
            print("iPhone XR")

        default:
            print("Unknown")
        }
    }

Objective-C:

if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    switch ((int)[[UIScreen mainScreen] nativeBounds].size.height) {
        case 1136:
            printf("iPhone 5 or 5S or 5C");
                break;

        case 1334:
            printf("iPhone 6/6S/7/8");
            break;

        case 1920, 2208:
            printf("iPhone 6+/6S+/7+/8+");
            break;

        case 2436:
            printf("iPhone X, XS");
            break;

        case 2688:
            printf("iPhone XS Max");
            break;

        case 1792:
            printf("iPhone XR");
            break;

        default:
            printf("Unknown");
            break;
    }
}

Xamarin.iOS:

if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone) {
    if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1136) {
        Console.WriteLine("iPhone 5 or 5S or 5C");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1334) {
        Console.WriteLine("iPhone 6/6S/7/8");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1920 || (UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2208) {
        Console.WriteLine("iPhone 6+/6S+/7+/8+");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2436) {
        Console.WriteLine("iPhone X, XS");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 2688) {
        Console.WriteLine("iPhone XS Max");
    } else if ((UIScreen.MainScreen.Bounds.Height * UIScreen.MainScreen.Scale) == 1792) {
        Console.WriteLine("iPhone XR");
    } else {
        Console.WriteLine("Unknown");
    }
}

На основании вашего вопроса следующим образом:

Или используйте screenSize.height как float 812.0f, а не int 812.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
        // 812.0 on iPhone X, XS
        // 896.0 on iPhone XS Max, XR.

    if (screenSize.height >= 812.0f)
        NSLog(@"iPhone X");
    }

Для получения дополнительной информации вы можете обратиться к следующей странице в Руководстве по интерфейсу пользователя iOS:

Swift:

Определить с помощью topNotch:

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

    return false
}

Objective-C:

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

    return  NO;
}

ОБНОВЛЕНИЕ:

Не используйте свойство userInterfaceIdiom для определения типа устройства, поскольку документация для userInterfaceIdiom объясняет:

Для универсальных приложений вы можете использовать это свойство, чтобы адаптировать поведение вашего приложения для определенного типа устройства. Например, устройства iPhone и iPad имеют разные размеры экрана, поэтому вам может потребоваться создать разные представления и элементы управления в зависимости от типа текущего устройства.

То есть это свойство просто используется для определения стиля представления запущенного приложения. Однако приложение iPhone (не универсальное) может быть установлено на устройстве iPad через App Store, в этом случае userInterfaceIdiom также вернет UIUserInterfaceIdiomPhone.

Правильный способ - получить имя машины через uname. Проверьте следующее для деталей:

306
Anbu.Karthik

Еще одна возможность, которая работает на iOS 11 и iOS 12, потому что iPhone X является единственным с надписью вверху и вставкой 44. Вот что я действительно обнаруживаю здесь:

Objective-C:

    BOOL iPhoneX = NO;
    if (@available(iOS 11.0, *)) {
        UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];
        if (mainWindow.safeAreaInsets.top > 24.0) {
            iPhoneX = YES;
        }
    }

Swift 4:

/// Has safe area
///
/// with notch: 44.0 on iPhone X, XS, XS Max, XR.
///
/// without notch: 20.0 on iPhone 8 on iOS 12+.
///
static var hasSafeArea: Bool {
    guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 24 else {
        return false
    }
    return true
}

И, конечно же, вам может понадобиться проверить левый и правый вкладыши безопасной зоны, если вы находитесь в альбомной ориентации.

Правка: _window является UIWindow AppDelegate, где эта проверка выполняется в приложении didFinishLaunchingWithOptions.

Ответ обновлен для iOS 12, чтобы проверить, является ли top> 24, а не top> 0.

Правка: В симуляторе вы можете перейти к Hardware, Toggle In-Call Status Bar. Это показывает, что высота строки состояния не изменяется на iPhone X на iOS 11 или iPhone XS iOS 12 при участии в вызове. Все, что изменяется, это значок времени, который в обоих случаях получает зеленый фон. Вот снимок:

 enter image description here

92
saswanb

Вы должны выполнить различные обнаружения 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
}
52
Cœur

Вы можете сделать это, чтобы обнаружить iPhone X устройство в соответствии с размером.

Swift

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     
}

 enter image description here

Но,

Это не достаточный способ. Что делать, если 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

41
Jaydeep

Проверьте модель устройства/имя машины , НЕ используйте количество точек/пикселей в вашем коде напрямую, это hard code и не имеет смысла для оборудования устройства.

#import <sys/utsname.h>

NSString* deviceName()
{
    struct utsname systemInfo;
    uname(&systemInfo);

    return [NSString stringWithCString:systemInfo.machine
                          encoding:NSUTF8StringEncoding];
}

Результат:

@"iPhone10,3" on iPhone X (CDMA)
@"iPhone10,6" on iPhone X (GSM)

Обратитесь к этот ответ .

Полная реализация кода:

#import <sys/utsname.h>

NSString * GetDeviceModel(void)
{
    static dispatch_once_t onceToken;
    static NSString *strModelID = nil;

    dispatch_once(&onceToken, ^{
#if TARGET_IPHONE_SIMULATOR
        strModelID = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];
#else
        struct utsname systemInfo;

        uname(&systemInfo);
        strModelID = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
#endif
    });

    return strModelID;
}

// See the `Hardware strings` in https://en.wikipedia.org/wiki/List_of_iOS_devices
BOOL IsiPhoneX(void)
{
    NSString *strModelID = GetDeviceModel();

    return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"];
}

BOOL IsNotchiPhone(void)
{
    NSString *strModelID = GetDeviceModel();

    return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"] || // iPhone X
           [strModelID isEqualToString:@"iPhone11,2"] || [strModelID isEqualToString:@"iPhone11,4"] || [strModelID isEqualToString:@"iPhone11,6"] || // iPhone XS (Max)
           [strModelID isEqualToString:@"iPhone11,8"]; // iPhone XR
}
36
Itachi
#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] bounds] .size.height == 812.0)

#define IS_IPHONE_XS      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)
#define IS_IPHONE_X_MAX      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 896.0)
#define IS_RETINA        ([[UIScreen mainScreen] scale] >= 2.0) // 3.0 for iPhone X, 2.0 for others

#define IS_IPAD_DEVICE   [(NSString*)[UIDevice currentDevice].model hasPrefix:@"iPad"]

Примечание: - будьте осторожны, он отлично работает только для портретной ориентации

25
Jagveer Singh

Посмотрев на все ответы, я сделал следующее:

Решение (совместимо с 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

(старый метод все еще будет работать, но этот новый метод более перспективен)

23
Cloud9999Strife

Все эти ответы, основанные на измерениях, подвержены некорректному поведению на будущих устройствах. Они будут работать сегодня, но что, если в следующем году появится iPhone того же размера, но с камерой и т.д. Под стеклом, чтобы не было «надреза»? Если единственный вариант - обновить приложение, тогда это плохое решение для вас и ваших клиентов.

Вы также можете проверить строку аппаратной модели, например «iPhone10,1», но это проблематично, потому что иногда Apple выпускает разные номера моделей для разных операторов по всему миру.

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

17
clarus

Быстрый ответ 4+

iPhone X, XR, XS, XSMAX: ​​

Примечание: для тестирования требуется реальное устройство

Ссылка

 let deviceType = UIDevice.current.modelName
        switch deviceType {
        case "iPhone10,3", "iPhone10,6":
            print("iPhoneX")
        case "iPhone11,2":
            print("iPhone XS")
        case "iPhone11,4":
            print("iPhone XS Max")
        case "iPhone11,6":
            print("iPhone XS Max China")
        case "iPhone11,8":
            print("iPhone XR")
        default:
            break
}

extension UIDevice {
    var modelName: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        return identifier
    }
}
10
Jack

Да, это возможно. Загрузите расширение UIDevice-Hardware (или установите через CocoaPod 'UIDevice-Hardware') и затем используйте:

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

Обратите внимание, что это не будет работать в симуляторе, только на реальном устройстве.

10
Hendrik

Я знаю, что это только 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.maxLength == 896.0
  static var hasNotch: Bool {
    return iPhoneX || iPhoneXRMax
  }
}

Тогда использовать это:

if DeviceType.hasNotch {
  print("This executes on all phones with a notch")
}

if DeviceType.iPhone678 {
  print("This executes on iPhones 6, 7 and 8")
}

Если вы используете LaunchImage в своем проекте, обязательно добавьте изображения для всех поддерживаемых устройств (например, XS Max, XR), потому что UIScreen.main.bounds не вернет правильное значение без них.

7
budidino

Swift 4 многоразовый удлинитель

    public extension UIDevice {

    public enum `Type` {
        case iPad
        case iPhone_unknown
        case iPhone_5_5S_5C
        case iPhone_6_6S_7_8
        case iPhone_6_6S_7_8_PLUS
        case iPhone_X_Xs
        case iPhone_Xs_Max
        case iPhone_Xr
    }

    public var hasHomeButton: Bool {
        switch type {
        case .iPhone_X_Xs, .iPhone_Xr, .iPhone_Xs_Max:
            return false
        default:
            return true
        }
    }

    public var type: Type {
        if userInterfaceIdiom == .phone {
            switch UIScreen.main.nativeBounds.height {
            case 1136:
                return .iPhone_5_5S_5C
            case 1334:
                return .iPhone_6_6S_7_8
            case 1920, 2208:
                return .iPhone_6_6S_7_8_PLUS
            case 2436:
                return .iPhone_X_Xs
            case 2688:
                return .iPhone_Xs_Max
            case 1792:
                return .iPhone_Xr
            default:
                return .iPhone_unknown
            }
        }
        return .iPad
   }
}
6
ale_stro

Согласно ответу @ saswanb, это версия Swift 4:

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

Все ответы, которые используют 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
    }
}
5
DevAndArtist

Не следует полагать, что единственным устройством, которое Apple выпускает с другой высотой UINavigationBar, будет iPhone X. Попробуйте решить эту проблему, используя более общее решение. Если вы хотите, чтобы полоса всегда была на 20px больше высоты по умолчанию, ваш код должен добавить 20px к высоте полосы, вместо того, чтобы устанавливать ее на 64px (44px + 20px).

4
IMcD23

Swift 3 + 4:

без необходимости какого-либо размера пикселя устройства

//UIApplication+SafeArea.Swift

extension UIApplication { 

    static var isDeviceWithSafeArea:Bool {

        if #available(iOS 11.0, *) {
            if let topPadding = shared.keyWindow?.safeAreaInsets.bottom,
                topPadding > 0 {
                return true
            }
        }

        return false
    }
}

Пример:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}
4
Peter Kreinz
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
Kiran Sarvaiya
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
3
alexander.pan

Обычно это нужно программисту для ограничения сверху или снизу, поэтому эти методы могут помочь

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
Andrey

Для тех, кто получает 2001px вместо 2436px для собственной высоты границ (как я), это потому, что вы создали свое приложение с более старым SDK, до iOS 11 (Xcode 8 вместо Xcode 9). В более старом SDK iOS будет отображать приложения «в черной рамке» на iPhone X вместо того, чтобы расширять экран от края до края за верхнюю «отметку сенсора». Это уменьшает размер экрана, поэтому это свойство возвращает 2001 вместо 2436.

Самое простое решение - просто проверить оба размера, если вас интересует только обнаружение устройства. Я использовал этот метод для определения FaceID при сборке со старым Xcode SDK, который не имеет значения ENUM, указывающего биометрический тип. В этой ситуации обнаружение устройства с использованием высоты экрана казалось лучшим способом узнать, было ли у устройства FaceID против TouchID без необходимости обновления Xcode.

3
Jon Summers
- (BOOL)isIphoneX {
    if (@available(iOS 11.0, *)) {
        UIWindow *window = UIApplication.sharedApplication.keyWindow;
        CGFloat topPadding = window.safeAreaInsets.top;
        if(topPadding>0) {
            return YES;
        }
        else {
            return NO;
        }
    }
    else {
        return NO;
    }
}
3
user6788419

Я подробно остановился на ваших ответах и ​​сделал расширение Swift для UIDevice. Мне нравятся Swift перечисления и "все в порядке" и атомизированные. Я создал решение, которое работает как на устройстве, так и на симуляторе. 

Преимущества: - простой интерфейс, использование, например 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
    }
}
2
deathhorse

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

Вместо этого я создал макрос для этой цели, он очень прост в использовании и использует аппаратные флаги для предотвращения вышеупомянутых проблем.

Правка: Обновлено для поддержки iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Использовать:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Да, действительно. 


Macro:

Просто скопируйте и вставьте это куда угодно, я предпочитаю самый низ моего .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]);\
})()\
)
2
Albert Renshaw

Я полагаюсь на высоту строки состояния строки состояния, чтобы определить, является ли это iPhone X:

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

Это для приложения и портрета. Вы также можете проверить размер в соответствии с ориентацией устройства. Кроме того, на других iPhone панель состояния может быть скрыта, поэтому высота кадра равна 0. На iPhone X строка состояния никогда не скрывается.

2
Tiois

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

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
rgkobashi

Кроме того, вы можете проверить ' DeviceKit ' pod . После установки, все, что вам нужно сделать, чтобы проверить устройство, это:

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

Я должен был решить ту же проблему недавно. И хотя на этот вопрос дан окончательный ответ («Нет»), это может помочь другим, кому нужно специфическое поведение макета 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
simeon

Есть несколько причин, чтобы узнать, что это за устройство.

  1. Вы можете проверить высоту устройства (и ширину). Это полезно для макета, но вы обычно не хотите делать это, если хотите знать точное устройство.

  2. Для макета вы также можете использовать UIView.safeAreaInsets.

  3. Если вы хотите отобразить имя устройства, например, для включения в электронное письмо для диагностических целей, после извлечения модели устройства с помощью sysctl (), вы можете использовать эквивалент этого, чтобы вычислить имя:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
    
0
Hwee-Boon Yar

Я думаю, что Apple не хочет, чтобы мы вручную проверяли, есть ли на устройстве "метка" или "домашний индикатор" но работает код:

-(BOOL)hasTopNotch{

    if (@available(iOS 11.0, *)) {

        float max_safe_area_inset = MAX(MAX([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top, [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.right),MAX([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.bottom, [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.left));

        return max_safe_area_inset >= 44.0;

    }

    return  NO;

}

-(BOOL)hasHomeIndicator{

    if (@available(iOS 11.0, *)) {

        int iNumberSafeInsetsEqualZero = 0;

        if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top == 0.0)iNumberSafeInsetsEqualZero++;
        if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.right == 0.0)iNumberSafeInsetsEqualZero++;
        if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.bottom == 0.0)iNumberSafeInsetsEqualZero++;
        if([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.left == 0.0)iNumberSafeInsetsEqualZero++;

        return iNumberSafeInsetsEqualZero <= 2;

    }

    return  NO;

}

Некоторые другие посты не работают. Например, iPhone 6S с «строкой состояния в режиме разговора» (зеленая полоса) в портретном режиме имеет безопасную вставку с большим верхом. С моим кодом все дела рассматриваются ( даже если устройство запускается в портретной или альбомной ориентации )

0
BIOS-K

Лучший и самый простой способ определить, является ли устройство iPhone X,

https://github.com/stephanheilner/UIDevice-DisplayName

var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8 , value != 0 else { return identifier}
            return identifier + String(UnicodeScalar(UInt8(value)))}

И идентификатор это либо "iPhone10,3", либо "iPhone10,6" для iPhone X.

0
satheeshwaran

В Portrait only я использую ширину и высоту рамки вида для проверки:

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

}

Размеры портретного экрана перечислены здесь

 enter image description here

0
Lance Samaria

С выпуском iOS 12 такие устройства, как iPhone X, могут попасть в эту категорию. 

extension UIDevice {
    var isPortrait: Bool {

       return UIDeviceOrientationIsPortrait(orientation) ||
      UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)

  }

   var isDeviceWith_XShape : Bool {

    if self.userInterfaceIdiom == .phone {

        if isPortrait
        {
            switch UIScreen.main.nativeBounds.height {

            case 2436,2688,1792:
                print("iPhone X, Xs, Xr, Xs Max")
                return true
            default:
                print("Any other device")
                return false
            }
        }
        else
        {
            switch UIScreen.main.nativeBounds.width {

            case 2436,2688,1792:
                print("iPhone X, Xs, Xr, Xs Max")
                return true
            default:
                print("Any other device")
                return false
            }
        }


    }
    else
    {
        return false
    }

}`
0
dev_shanghai

Для быстрого решения мне нравится это:

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

Чтобы обнаружить любое из устройств, используя простые методы. как ниже,

func isPhoneDevice() -> Bool {
    return UIDevice.current.userInterfaceIdiom == .phone
}

func isDeviceIPad() -> Bool {
    return UIDevice.current.userInterfaceIdiom == .pad
}

func isPadProDevice() -> Bool {
    let SCREEN_WIDTH: CGFloat = UIScreen.main.bounds.size.width
    let SCREEN_HEIGHT: CGFloat = UIScreen.main.bounds.size.height
    let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)

    return UIDevice.current.userInterfaceIdiom == .pad && SCREEN_MAX_LENGTH == 1366.0
}

func isPhoneXandXSDevice() -> Bool {
    let SCREEN_WIDTH = CGFloat(UIScreen.main.bounds.size.width)
    let SCREEN_HEIGHT = CGFloat(UIScreen.main.bounds.size.height)
    let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)

    return UIDevice.current.userInterfaceIdiom == .phone && SCREEN_MAX_LENGTH == 812.0
}

func isPhoneXSMaxandXRDevice() -> Bool {
    let SCREEN_WIDTH = CGFloat(UIScreen.main.bounds.size.width)
    let SCREEN_HEIGHT = CGFloat(UIScreen.main.bounds.size.height)
    let SCREEN_MAX_LENGTH: CGFloat = fmax(SCREEN_WIDTH, SCREEN_HEIGHT)

    return UIDevice.current.userInterfaceIdiom == .phone && SCREEN_MAX_LENGTH == 896.0
}

и звони так,

if isPhoneDevice() {
     // Your code
}
0
User558