Detecta si el dispositivo es iPhone X

262

Mi aplicación iOS utiliza una altura personalizada para lo UINavigationBarque genera algunos problemas en el nuevo iPhone X.

¿Alguien sabe cómo detectar de manera confiable mediante programación (en Objective-C) si una aplicación se ejecuta en iPhone X?

EDITAR:

Por supuesto, es posible verificar el tamaño de la pantalla, sin embargo, me pregunto si hay algún método "integrado" TARGET_OS_IPHONEpara detectar iOS ...

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    if (screenSize.height == 812)
        NSLog(@"iPhone X");
}

EDITAR 2:

No creo que mi pregunta sea un duplicado de la pregunta vinculada. Por supuesto, hay métodos para "medir" diferentes propiedades del dispositivo actual y usar los resultados para decidir qué dispositivo se usa. Sin embargo, este no era el punto real de mi pregunta, ya que traté de enfatizar en mi primera edición.

La pregunta real es: "¿Es posible detectar directamente si el dispositivo actual es un iPhone X (por ejemplo, mediante alguna función del SDK) o tengo que usar mediciones indirectas" ?

Según las respuestas dadas hasta ahora, supongo que la respuesta es "No, no hay métodos directos. Las medidas son el camino a seguir".

Andrei Herford
fuente
iPhone X tiene una resolución de pantalla diferente a las demás.
El Tomato
2
Sí, como mencioné en mi edición, es posible verificar el tamaño de la pantalla. Sin embargo, la pregunta es, si hay un método "directo" para consultar el tipo de dispositivo en lugar de "indirectas" ...
Andrei Herford
3
El autor solo quiere obtener el tipo de dispositivo, no la resolución de la pantalla. ¿Por qué no verifica el nombre de la máquina directamente? @lubilis tiene razón.
Itachi
2
¿por qué no usas las guías de área segura como lo recomienda Apple?
holex
44
IMPORTANTE, futuros desarrolladores: no detecten esto utilizando la altura de la pantalla como sugieren las principales soluciones actuales, es malo ya que puede dar lugar a falsos positivos para dispositivos futuros; no funcionará si UIWindow aún no se ha procesado (como en las funciones de inicio de AppDelegate), no funcionará en aplicaciones horizontales y puede fallar en el simulador si se establece la escala. ¡NUNCA use números mágicos para cosas como esta! Puede verificar las marcas de hardware para garantizar el éxito como lo he hecho aquí: stackoverflow.com/a/51511947/2057171
Albert Renshaw

Respuestas:

384

Según su pregunta, la respuesta es no. No hay métodos directos. Para obtener más información, puede obtener la información aquí:

y

La altura del iPhone X es de 2436 px

Desde tamaños de pantalla del dispositivo y resoluciones :

ingrese la descripción de la imagen aquí

Desde los tamaños y orientaciones de la pantalla del dispositivo :

ingrese la descripción de la imagen aquí

Swift 3 y posterior :

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

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

Basado en su pregunta de la siguiente manera:

O utilizar screenSize.heightcomo flotante 812.0f no 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");
    }

Para obtener más información, puede consultar la siguiente página en las Directrices de interfaz humana de iOS:

Rápido :

Detectar con topNotch:

Si alguien considera usar la muesca para detectar iPhoneX, tenga en cuenta que en "paisaje" es lo mismo para todos los iPhones.

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
}

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

ACTUALIZACIÓN :

No use la userInterfaceIdiompropiedad para identificar el tipo de dispositivo, como explica la documentación de userInterfaceIdiom :

Para aplicaciones universales, puede usar esta propiedad para adaptar el comportamiento de su aplicación para un tipo específico de dispositivo. Por ejemplo, los dispositivos iPhone y iPad tienen diferentes tamaños de pantalla, por lo que es posible que desee crear diferentes vistas y controles según el tipo de dispositivo actual.

Es decir, esta propiedad solo se usa para identificar el estilo de vista de la aplicación en ejecución. Sin embargo, la aplicación de iPhone (no la universal) podría instalarse en el dispositivo iPad a través de la tienda de aplicaciones, en ese caso, userInterfaceIdiomdevolverá elUIUserInterfaceIdiomPhone .

La forma correcta es obtener el nombre de la máquina a través de uname. Verifique lo siguiente para más detalles:

Anbu.Karthik
fuente
La resolución del iPhone X es de 2436 x 1125 píxeles de acuerdo con: iphonesoft.fr/2017/09/12/…
Medhi
1
@Medhi - la resolución del iPhone X es - 1125 x 2436 píxeles (~ 458 ppi de densidad de píxeles)
Anbu.Karthik
14
¡NO! La aplicación de iPhone (no el universo) podría instalarse en el dispositivo iPad a través de la tienda de aplicaciones, en ese caso,userInterfaceIdiomtambién devolverá elUIUserInterfaceIdiomPhone. Esta respuesta es incorrecta.
Itachi
1
@ThreeCoins, actualice su respuesta para dispositivos plus según la sugerencia de Leo Dabus. Funciona en el simulador Plus pero no en el dispositivo.
Hiren Gujarati
2
Esto es malo ya que puede dar lugar a falsos positivos para dispositivos futuros; no funcionará si UIWindow aún no se ha procesado (AppDelegate), no funcionará en aplicaciones horizontales y puede fallar en el simulador si se establece la escala. Puede verificar las marcas de hardware para garantizar el éxito como lo he hecho aquí: stackoverflow.com/a/51511947/2057171
Albert Renshaw
101

Otra posibilidad, que funciona en iOS 11 y iOS 12 porque el iPhone X es el único con una muesca en la parte superior y un recuadro de 44. Eso es lo que realmente estoy detectando aquí:

C objetivo:

    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
}

Y, por supuesto, es posible que deba verificar las inserciones del área segura izquierda y derecha si está en orientación horizontal.

Editar: _window es la UIWindow de AppDelegate, donde esta comprobación se realiza en la aplicación didFinishLaunchingWithOptions.

Respuesta actualizada para iOS 12 para verificar si top> 24 en lugar de top> 0.

Editar: en el simulador puede ir a Hardware, Activar barra de estado de llamada entrante. Hacer eso me muestra que la altura de la barra de estado no cambia en el iPhone X en iOS 11 o iPhone XS iOS 12 cuando se realiza una llamada. Todo lo que cambia es el ícono de tiempo, que obtiene un fondo verde, en ambos casos. Aquí hay un complemento:

ingrese la descripción de la imagen aquí

Saswanb
fuente
55
Las inserciones de área segura contendrán la altura de la barra de estado, si está visible, en otros dispositivos. Comprobar si es 0 solo le indicará si la barra de estado está visible, no si el dispositivo es un iPhone X.
IMcD23
3
"Esto puede romperse en iPhone Xs o iPhone 11 ", dijo Cook.
Itachi
11
Me he adaptado un poco y uso if _window.safeAreaInsets != UIEdgeInsets.zeropara permitir cualquier orientación del dispositivo
Fraser
2
Si no desea usar .top, safeAreaInsets.bottomserá 34 en iPhone X y 0 en otros dispositivos
blwinters
77
Advertencia: no use esto, se rompe en iOS 12. Tampoco está documentado qué debe hacer UIWindow en este caso. openradar.appspot.com/42372793
steipete
73

Deberá realizar diferentes detecciones de iPhone X según la necesidad real.

para tratar con la muesca superior (barra de estado, barra de navegación), etc.

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
}

para tratar con el indicador de inicio inferior (pestaña), etc.

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
}

para tamaño de fondos, funciones de pantalla completa, etc.

class var isIphoneXOrBigger: Bool {
    // 812.0 on iPhone X, XS.
    // 896.0 on iPhone XS Max, XR.
    return UIScreen.main.bounds.height >= 812
}

Nota: eventualmente mézclelo con UIDevice.current.userInterfaceIdiom == .phone
Nota: este método requiere tener un guión gráfico de LaunchScreen o imágenes de lanzamiento adecuadas

para relación de fondos, funciones de desplazamiento, etc.

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
}

Nota: este método requiere tener un guión gráfico de LaunchScreen o imágenes de lanzamiento adecuadas

para análisis, estadísticas, seguimiento, etc.

Obtenga el identificador de la máquina y compárelo con los valores documentados:

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

Para incluir el simulador como un iPhone X válido en sus análisis:

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

Para incluir iPhone XS, XS Max y XR, simplemente busque modelos que comiencen con "iPhone11":

return model == "iPhone10,3" || model == "iPhone10,6" || model.starts(with: "iPhone11,")

para soporte de 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
}
Coeur
fuente
Esperaba que return LAContext().biometryType == .typeFaceIDfuncionara incluso si el usuario hubiera denegado canEvaluatePolicy, pero no funciona para mí, todavía regresa.none
Jeremy
Bueno @Jeremy, es un comportamiento documentado, consecuencia de la política de privacidad de Apple. Es por eso que el comentario sobre el método.
Cœur
Ah, interpreté mal tu comentario. Pensé que querías decir que usar canEvaluatePolicy podría fallar, así que usa lo siguiente en su lugar. Me resulta un poco extraño que se le permita verificar si el dispositivo tiene Face ID hasta que el usuario responda al alternar y luego ni siquiera puede verificarlo. ¿Cómo se supone que debo proporcionar un mensaje de error útil para decir que vaya a Configuración y alterne Face ID?
Jeremy
@ Jeremy No tengo un iPhone X, así que no lo sé. Tal vez podría usar la detección del modelo anterior ( model == "iPhone10,3" || model == "iPhone10,6"), y si canUseFaceIDdevuelve falso, significa que fue rechazado por el usuario.
Cœur
1
@MateoOlaya Nada en mi respuesta sería rechazado por Apple: puedes usarlo todo.
Coeur
42

Puede hacer esto para detectar el dispositivo iPhone X según la dimensión.

Rápido

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

C objetivo

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

ingrese la descripción de la imagen aquí

pero ,

Esto no es suficiente. ¿Qué pasaría si Apple anunciara el próximo iPhone con la misma dimensión que el iPhone X? Entonces, la mejor manera es usar una cadena de hardware para detectar el dispositivo.

Para el dispositivo más nuevo, la cadena de hardware es la siguiente.

iPhone 8 - iPhone10,1 o iPhone 10,4

iPhone 8 Plus - iPhone10,2 o iPhone 10,5

iPhone X - iPhone10,3 o iPhone10,6

Jaydeep
fuente
2
Debería usar en [UIDevice currentDevice]lugar de[[UIDevice alloc] init]
S. Matsepura
el único problema con la cadena de hardware es que no funciona en el simulador
quemeful
38

Verifique el nombre del modelo / máquina del dispositivo , NO use el recuento de puntos / píxeles en su código directamente, es un código duro y no tiene sentido para el hardware del dispositivo , el modelo del dispositivo es el único identificador único para un tipo de dispositivo que coincida .

#import <sys/utsname.h>

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

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

Resultado:

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

Refiérase a esta respuesta .

Implementación de código completo:

#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()];
}
Itachi
fuente
1
Excelente respuesta ya que maneja el simulador correctamente. Agregue la línea #importación a la sección "código completo". Me perdí eso (copiar / pegar) en mi primer intento.
mpoisot
1
Ese es mi método preferido. Consulte esta wiki para obtener una lista completa de cadenas de modelos de dispositivos. Como comentario adicional, @ "iphone10,3" también se puede ver como código duro.
YvesLeBorg
1
@YvesLeBorg Sí, es realmente un tema controvertido crítico. La cadena del modelo de hardware tiene un identificador único que los puntos de pantalla para el dispositivo, creo. En general, se usa para estadísticas de datos.
Itachi
25
#define IS_IPHONE        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS  (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_IPHONE_X      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)

define IS_IPHONE_X (límites de 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"]

Nota: tenga cuidado, funciona bien solo para orientación vertical

Jagveer Singh
fuente
2
Tenga cuidado, funciona bien solo para orientación vertical
CFIFok
1
Gracias por esto. Funciona bien. En el modo horizontal necesita ajustar esos números. El número mágico de iPhoneX en modo horizontal es 375.0
pvella
Hay algunos iPhone Plus / Max / Pro usando nativeScalecon 3.0, ¿verdad?
Itachi
24

Después de ver todas las respuestas, esto es lo que terminé haciendo:

Solución (compatible con 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
    }
}

Utilizar

if UIDevice.isIphoneX {
    // is iPhoneX
} else {
    // is not iPhoneX
}

Nota

Pre Swift 4.1 puede verificar si la aplicación se ejecuta en un simulador de esta manera:

TARGET_OS_SIMULATOR != 0

A partir de Swift 4.1 en adelante, puede verificar si la aplicación se está ejecutando en un simulador utilizando la condición de plataforma del entorno Target :

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

(el método anterior seguirá funcionando, pero este nuevo método es más a prueba de futuro)

Cloud9999Strife
fuente
¿Apple estará bien con esto?
Surjeet Rajput
@ comando24 Sí, no veo ninguna razón para que rechacen la aplicación debido a este código.
Cloud9999Strife
18

Todas estas respuestas basadas en dimensiones son susceptibles a un comportamiento incorrecto en dispositivos futuros. Funcionarán hoy, pero ¿qué pasa si el próximo año hay un iPhone del mismo tamaño pero tiene la cámara, etc. debajo del cristal para que no haya "muesca"? Si la única opción es actualizar la aplicación, entonces es una solución pobre para usted y sus clientes.

También puede verificar la cadena del modelo de hardware como "iPhone10,1", pero eso es problemático porque a veces Apple lanza diferentes números de modelo para diferentes operadores de todo el mundo.

El enfoque correcto es rediseñar el diseño superior o resolver los problemas que tiene con la altura de la barra de navegación personalizada (eso es en lo que me enfocaría). Pero, si decide no hacer ninguna de esas cosas, tenga en cuenta que lo que sea que esté haciendo es un truco para que esto funcione hoy , y tendrá que corregirlo en algún momento, tal vez varias veces, para mantener los trucos. trabajando.

clarus
fuente
1
Correcto. Refinando una suposición de que el número X siempre será A a uno, ese número X siempre será A, a menos que la condición Y cuando sea B solo esté cavando más profundo. Tamaño basado en el área segura nominada por Apple, no adivinando.
Tommy
2
Me preocuparé por el próximo iPhone cuando esté disponible. Quiero que mi aplicación funcione HOY.
Vahid Amiri
13

SWIFT 4+ Respuesta

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

Nota: necesita un dispositivo real para la prueba

Referencia

 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
    }
}
Jack
fuente
Para el método 1, puede quitar la propiedad "var window" fuera del func y solo una constante "let" dentro de él (escriba UIWindow, es decir, no es opcional). Me gusta esta respuesta ya que al inicio, self.view.window puede ser nulo, y UIApplication.shared.keyWindow también puede ser nulo, mientras que crear una UIWindow de esta manera funciona siempre.
Rolleric
11

Extensión reutilizable SWIFT 4/5 con soporte para 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
   }
}
ale_stro
fuente
2
buena extensión, pero lo más útil aquí esUIDevice.current.hasHomeButton
WINSergey
1
@ale_stro ¿es bueno usar userInterfaceIdiom para determinar los dispositivos para la aplicación universal? la mayoría de la gente no recomienda esto. ¿Hay algún daño al usarlo?
Shaqir dijo el
10

Sí, es posible. Descargue la extensión UIDevice-Hardware (o instálela a través de 'UIDevice-Hardware' de CocoaPod) y luego use:

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

Tenga en cuenta que esto no funcionará en el simulador, solo en el dispositivo real.

Hendrik
fuente
Todo el código del dispositivo aquí: iphonesoft.fr/2016/10/31/… Ejemplo: iPhone X: iPhone10,5 y iPhone10,6
Medhi
Las cadenas de hardware de wikipedia decían "iPhone10,3 y iPhone10,6". @Medhi
Itachi
@ Medhi, puedes usarlo ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]en Simulator para obtener los valores reales de Xcode.
Coeur
9

Según la respuesta de @ saswanb, esta es una versión de Swift 4:

var iphoneX = false
if #available(iOS 11.0, *) {
    if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
        iphoneX = true
    }
}
MattOZ
fuente
¡La barra de estado también se considera fuera del área segura! ¡Esto devolverá falsos positivos! Debe ser superior a 20 puntos (altura de la barra de estado). Esto también es cierto si el dispositivo es iPhone Xs, R o Xs Max.
MQoder
el código funciona muy bien, pero tenga cuidado: keyWindowes nilhasta que el controlador de vista principal ha llamadoviewDidAppear
Casey
9

Sé que es solo una solución Swift , pero podría ayudar a alguien.

Tengo globals.swiften cada proyecto y una de las cosas que siempre agrego es DeviceTypedetectar fácilmente el dispositivo del usuario:

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

Luego para usarlo:

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

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

Si lo usa LaunchImageen su proyecto, asegúrese de agregar imágenes para todos los dispositivos compatibles (como XS Max, XR) porque UIScreen.main.boundsno devolverá el valor adecuado sin ellos.

budidino
fuente
1
Un amigo nuevo en Swift preguntó cómo usar esto, en caso de que alguien más no lo sepa ... if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling
5

Todas las respuestas que están usando heightson solo la mitad de la historia por una razón. Si va a verificar así cuando la orientación del dispositivo es landscapeLefto landscapeRightla verificación fallará, porque heightse cambia con la width.

Es por eso que mi solución se ve así en 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
    }
}
DevAndArtist
fuente
Solo use nativeBounds en su lugar
Leo Dabus
4

No debe suponer que el único dispositivo que Apple lanzará con una altura diferente de UINavigationBar será el iPhone X. Intente resolver este problema utilizando una solución más genérica. Si desea que la barra siempre sea 20px más grande que su altura predeterminada, su código debe agregar 20px a la altura de la barra, en lugar de establecerla en 64px (44px + 20px).

IMcD23
fuente
Entonces, ¿qué otra solución tienes que proponer?
Stephane Mathis
@xaphod hay mejores respuestas ahora.
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
}
Kiran Sarvaiya
fuente
4

Swift 3 + 4:

sin necesidad de ningún valor de píxel de tamaño de dispositivo

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

Ejemplo:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}
Peter Kreinz
fuente
3
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
alexander.pan
fuente
2
te devolverá 812 si cargas la imagen predeterminada para iPhone X. Hasta entonces creo que te devolverá el tamaño de iPhone 7, aunque no estoy seguro ...
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;
    }
}
usuario6788419
fuente
1
¡La mejor respuesta! Sin necesidad de ningún valor de píxel de tamaño de dispositivo.
Peter Kreinz
3

Por lo general, el programador lo necesita para limitar la parte superior o inferior, por lo que estos métodos pueden ayudar

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
}

Para antes del iPhone X, estos métodos regresan: 0

Para iPhone X: 44 y 34 en consecuencia

Luego solo agregue estos extras a las restricciones superiores o inferiores

Andrey
fuente
3

Para aquellos que obtienen 2001px en lugar de 2436px para la altura de los límites nativos (como yo), es porque construiste tu aplicación con un SDK anterior, antes de iOS 11 (Xcode 8 en lugar de Xcode 9). Con un SDK anterior, iOS mostrará las aplicaciones "en caja negra" en el iPhone X en lugar de extender la pantalla de borde a borde, más allá de la "muesca del sensor" superior. Esto reduce el tamaño de la pantalla, razón por la cual esa propiedad devuelve 2001 en lugar de 2436.

La solución más simple es verificar ambos tamaños si solo está interesado en la detección de dispositivos. Utilicé este método para detectar FaceID mientras construía con un Xcode SDK anterior que no tiene el valor ENUM que especifica el tipo biométrico. En esta situación, la detección del dispositivo usando la altura de la pantalla parecía ser la mejor manera de saber si el dispositivo tenía FaceID vs TouchID sin tener que actualizar Xcode.

Jon Summers
fuente
3

NO use el tamaño de píxel de la pantalla como lo han sugerido otras soluciones, esto es malo ya que puede dar lugar a falsos positivos para dispositivos futuros; no funcionará si UIWindow aún no se ha procesado (AppDelegate), no funcionará en aplicaciones horizontales y puede fallar en el simulador si se establece la escala.

En cambio, hice una macro para este propósito, es muy fácil de usar y se basa en indicadores de hardware para evitar los problemas antes mencionados.

Editar: actualizado para admitir iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Usar:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Sí, de verdad.


Macro:

Simplemente copie y pegue esto en cualquier lugar, prefiero la parte inferior de mi archivo .h después @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]);\
})()\
)
Albert Renshaw
fuente
La única razón por la que puedo pensar para detectar iPhoneX es evitar la muesca en la parte superior de la pantalla; si es así, puede consultar safeArea.top para detectar el tamaño de dicha muesca. Solo asegúrese de medirlo después de que UIWindow se haya cargado, por lo que no durante viewDidLoad sino un ciclo de subprocesos después de:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw
2

Expliqué tus respuestas sobre las de los demás e hice una rápida extensión en UIDevice. Me gustan las enumeraciones rápidas y "todo en orden" y atomizado. He creado una solución que funciona tanto en el dispositivo como en el simulador.

Ventajas: - interfaz simple, uso, por ejemplo UIDevice.current.isIPhoneX - UIDeviceModelTypeenum le brinda la capacidad de extender fácilmente las características y constantes específicas del modelo que desea usar en su aplicación, por ejemplo, cornerRadius

Desventaja: es una solución específica del modelo, no específica de la resolución, por ejemplo, si Apple producirá otro modelo con las mismas especificaciones, esto no funcionará correctamente y debe agregar otro modelo para que funcione => necesita actualizar su aplicación

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
    }
}
caballo de la muerte
fuente
En lugar de usar Mirror, será más rápido usarlo sysctlbynamecomo se hizo en la respuesta Cloud9999Strife (y en mi respuesta también).
Coeur
2

Confío en la altura del marco de la barra de estado para detectar si se trata de un iPhone X:

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

Esto es para la aplicación un retrato. También puede verificar el tamaño de acuerdo con la orientación del dispositivo. Además, en otros iPhones, la barra de estado puede estar oculta, por lo que la altura del marco es 0. En iPhone X, la barra de estado nunca está oculta.

Tiois
fuente
Puede ocultar la barra de estado de iPhoneX controllercon esto: - (BOOL)prefersStatusBarHidden { return YES; } entonces la altura de la barra de estado es 0.
无 夜 之 星辰
@ 无 夜 之 星辰 Compruebo esto en el momento del arranque en AppDelegate.
Tiois
2

Estaba usando el código de Peter Kreinz (porque estaba limpio e hizo lo que necesitaba) pero luego me di cuenta de que funciona justo cuando el dispositivo está en posición vertical (dado que el relleno superior estará en la parte superior, obviamente) Así que creé una extensión para manejar todo orientaciones con sus respectivos rellenos, sin retransmitir en el tamaño de la pantalla:

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

Y en su sitio de llamadas simplemente:

let res = UIDevice.current.isIphoneX
rgkobashi
fuente
2

Alternativamente, puede consultar el pod ' DeviceKit '. Una vez instalado, todo lo que necesita hacer para verificar el dispositivo es:

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

Noviembre 2019:

Esto es lo que uso en todos mis proyectos de producción. Tenga en cuenta que esta esencia es bastante larga.

  1. Esto no usa cálculos de ancho o alto, sino más bien:
  2. Comprueba el modelo de cadena del dispositivo.
  3. No tiene el riesgo de que Apple rechace su compilación debido al uso de API privadas / indocumentadas.
  4. Funciona con simuladores 💯

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

Uso: let inset: CGFloat = DeviceUtility.isIphoneXType? 50.0: 40.0

Glenn
fuente
Funciona perfectamente. Gracias. Lo estoy usando en un proyecto SwiftUI.
LondonGuy
1

Tuve que resolver el mismo problema recientemente. Y aunque esta pregunta se responde definitivamente ("No"), esto puede ayudar a otros que necesitan un comportamiento de diseño específico para iPhone X.

No estaba realmente interesado en saber si el dispositivo era iPhone X. Estaba interesado en saber si el dispositivo tenía una pantalla con muescas.

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
}

También podría escribir una hasOnScreenHomeIndicatorvariable a lo largo de las mismas líneas (aunque, tal vez, verifique el área segura inferior).

Lo anterior usa mi extensión activada UIViewpara un acceso conveniente a las inserciones de área segura en iOS 10 y versiones anteriores.

@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
        }
    }
}
simeon
fuente
1

En Vertical solo uso el ancho y la altura del marco de la vista para verificar:

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

}

Las dimensiones de la pantalla vertical se enumeran aquí.

ingrese la descripción de la imagen aquí

Lance Samaria
fuente
0

Hay varias razones para querer saber qué es el dispositivo.

  1. Puede verificar la altura del dispositivo (y el ancho). Esto es útil para el diseño, pero generalmente no desea hacerlo si desea conocer el dispositivo exacto.

  2. Para fines de diseño, también puede usar UIView.safeAreaInsets.

  3. Si desea mostrar el nombre del dispositivo, por ejemplo, para que se incluya en un correo electrónico con fines de diagnóstico, después de recuperar el modelo del dispositivo usando sysctl (), puede usar el equivalente de este para calcular el nombre:

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