Обнаруживать, если устройство iPhone X

Мое приложение 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), или мне нужно использовать косвенные измерения"?

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

Ответ 1

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

и

Высота 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/11 Pro")

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

        case 1792:
            print("iPhone XR/ 11 ")

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

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

        case 1792:
            print("iPhone XR/ 11 ");
             break;

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

Xamarin.iOS:

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

На основании вашего вопроса:

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

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

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

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

Swift:

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

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

    return false
}

Objective-C:

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

    return  NO;
}

UPDATE:

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

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

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

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

Ответ 2

Еще одна возможность, которая работает на 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

Ответ 3

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

для работы с верхней меткой (панель состояния, navbar) и т.д.

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
}

Ответ 4

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

стриж

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

Цель - C

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

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

Ответ 5

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

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

Ответ 6

#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"]

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

Ответ 7

Посмотрев на все ответы, это то, что я закончил:

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

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

Ответ 8

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

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

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

Ответ 9

SWIFT 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

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

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

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

Ответ 11

В соответствии с ответом @saswanb это версия Swift 4:

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

Ответ 12

Расширительное расширение 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
   }
}

Ответ 13

Я знаю, что это всего лишь Swift решение, но оно может кому-то помочь.

У меня есть globals.swift в каждом проекте, и одна из вещей, которые я всегда добавляю, это DeviceType, чтобы легко определять пользовательское устройство:

struct ScreenSize {
  static let width = UIScreen.main.bounds.size.width
  static let height = UIScreen.main.bounds.size.height
  static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
  static let maxWH = max(ScreenSize.width, ScreenSize.height)
}

struct DeviceType {
  static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH < 568.0
  static let iPhone5orSE   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 568.0
  static let iPhone678     = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 667.0
  static let iPhone678p    = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 736.0
  static let iPhoneX       = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 812.0
  static let iPhoneXRMax   = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxWH == 896.0
  static var hasNotch: Bool {
    return iPhoneX || iPhoneXRMax
  }
}

Затем использовать его:

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

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

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

Ответ 14

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

Ответ 15

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

Ответ 16

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
}

Ответ 17

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
}

Ответ 18

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

Ответ 19

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

Ответ 20

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

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 соответственно

Затем просто добавьте эти дополнения в верхние или нижние ограничения

Ответ 21

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

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

Ответ 22

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

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

Изменение: Обновлено для поддержки iPhone, 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]);\
})()\
)

Ответ 23

Я подробно остановился на ваших ответах на других языках и быстро расширил 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
    }
}

Ответ 24

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

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

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

Ответ 25

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

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

Ответ 26

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

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

Ответ 27

Мне пришлось решить ту же проблему в последнее время. И хотя на этот вопрос однозначно ответил ("Нет"), это может помочь другим, которым требуется конкретное поведение макета 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 и более ранних UIView.

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

Ответ 28

Только в портрете я использую ширину и высоту рамки просмотра, чтобы проверить:

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

Ответ 29

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

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

  • В целях компоновки вы также можете использовать UIView.safeAreaInsets.

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

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

Ответ 30

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

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