Détecter si l'appareil est iPhone X


263

Mon application iOS utilise une hauteur personnalisée pour UINavigationBar qui entraîne des problèmes sur le nouvel iPhone X.

Quelqu'un sait-il déjà comment détecter de manière fiable par programme (dans Objective-C) si une application fonctionne sur iPhone X?

ÉDITER:

Bien sûr, la vérification de la taille de l'écran est possible, cependant, je me demande s'il existe une méthode "build in" comme TARGET_OS_IPHONEpour détecter iOS ...

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

EDIT 2:

Je ne pense pas que ma question soit un double de la question liée. Bien sûr, il existe des méthodes pour «mesurer» les différentes propriétés de l'appareil actuel et utiliser les résultats pour décider quel appareil est utilisé. Cependant, ce n'était pas le point réel de ma question comme j'ai essayé de le souligner dans mon premier montage.

La vraie question est: "Est-il possible de détecter directement si l'appareil actuel est un iPhone X (par exemple par une fonction SDK) ou dois-je utiliser des mesures indirectes" ?

D'après les réponses données jusqu'à présent, je suppose que la réponse est "Non, il n'y a pas de méthode directe. Les mesures sont la voie à suivre".


L'iPhone X a une résolution d'écran différente des autres.
El Tomato

2
Oui, comme je l'ai mentionné dans mon montage, il est possible de vérifier la taille de l'écran. Cependant, la question est de savoir s'il existe une méthode "directe" pour interroger le type d'appareil plutôt que des mesures "indirectes" ...
Andrei Herford

3
L'auteur veut juste obtenir le type d'appareil, pas la résolution d'écran. Pourquoi ne pas vérifier directement le nom de la machine? @lubilis a raison.
Itachi

2
pourquoi ne pas simplement utiliser les guides de zone de sécurité comme cela est recommandé par Apple?
holex

4
IMPORTANT, futurs développeurs: ne détectez pas cela en utilisant la hauteur de l'écran comme le suggèrent les principales solutions actuelles, c'est mauvais car cela peut entraîner des faux positifs pour les futurs appareils; ne fonctionnera pas si UIWindow n'a pas encore été rendu (comme dans vos fonctions d'initialisation AppDelegate), ne fonctionnera pas dans les applications de paysage et peut échouer sur le simulateur si l'échelle est définie. N'utilisez JAMAIS de nombres magiques pour des choses comme ça! Vous pouvez vérifier les indicateurs matériels pour garantir le succès comme je l'ai fait ici: stackoverflow.com/a/51511947/2057171
Albert Renshaw

Réponses:


385

Sur la base de votre question, la réponse est non. Il n'y a pas de méthode directe. Pour plus d'informations, vous pouvez obtenir les informations ici:

et

La hauteur de l'iPhone X est de 2436 px

À partir des tailles et résolutions d'écran de l'appareil :

entrez la description de l'image ici

À partir des tailles d'écran et des orientations de l'appareil :

entrez la description de l'image ici

Swift 3 et versions ultérieures :

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

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

Basé sur votre question comme suit:

Ou utilisez screenSize.heightcomme flotteur 812.0f non 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");
    }

Pour plus d'informations, vous pouvez consulter la page suivante dans les instructions relatives à l'interface humaine iOS:

Rapide :

Détecter avec topNotch:

Si quelqu'un envisage d'utiliser l'encoche pour détecter iPhoneX, sachez que sur "paysage", c'est la même chose pour tous les 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
}

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

MISE À JOUR :

N'utilisez pas la userInterfaceIdiompropriété pour identifier le type de périphérique, comme l' explique la documentation de userInterfaceIdiom :

Pour les applications universelles, vous pouvez utiliser cette propriété pour personnaliser le comportement de votre application pour un type de périphérique spécifique. Par exemple, les appareils iPhone et iPad ont des tailles d'écran différentes, vous pouvez donc vouloir créer des vues et des contrôles différents en fonction du type de l'appareil actuel.

Autrement dit, cette propriété est simplement utilisée pour identifier le style d'affichage de l'application en cours d'exécution. Cependant, l'application iPhone (pas la version universelle) pourrait être installée sur l'appareil iPad via l'App Store, dans ce cas, userInterfaceIdiomleUIUserInterfaceIdiomPhone également.

La bonne façon est d'obtenir le nom de la machine via uname. Vérifiez les éléments suivants pour plus de détails:


La résolution de l'iPhone X est de 2436 x 1125 pixels selon: iphonesoft.fr/2017/09/12/…
Medhi

1
@Medhi - la résolution de l'iphone X est de - 1125 x 2436 pixels (~ 458 densité de pixels ppp)
Anbu.Karthik

14
NON! L'application iPhone (et non l'univers) pourrait être installée sur un appareil iPad via l'App Store, dans ce cas, lauserInterfaceIdiomretourneraUIUserInterfaceIdiomPhoneégalement. Cette réponse est fausse.
Itachi

1
@ThreeCoins, veuillez mettre à jour votre réponse pour les appareils plus selon la suggestion de Leo Dabus. Il fonctionne sur le simulateur Plus mais pas sur l'appareil.
Hiren Gujarati

2
C'est mauvais car cela peut entraîner des faux positifs pour les futurs appareils; ne fonctionnera pas si UIWindow n'a pas encore été rendu (AppDelegate), ne fonctionnera pas dans les applications de paysage et peut échouer sur le simulateur si l'échelle est définie. Vous pouvez vérifier les indicateurs matériels pour garantir le succès comme je l'ai fait ici: stackoverflow.com/a/51511947/2057171
Albert Renshaw

102

Autre possibilité, qui fonctionne sur iOS 11 et iOS 12 car l'iPhone X est le seul avec une encoche en haut et un encart de 44. C'est ce que je détecte vraiment ici:

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

Et bien sûr, vous devrez peut-être vérifier les encarts de zone de sécurité gauche et droite si vous êtes en orientation paysage.

Edit: _window est la UIWindow de l'AppDelegate, où cette vérification est effectuée dans l'application didFinishLaunchingWithOptions.

Réponse mise à jour pour iOS 12 pour vérifier si top> 24 plutôt que top> 0.

Modifier: dans le simulateur, vous pouvez aller dans Matériel, Basculer la barre d'état en cours d'appel. Cela me montre que la hauteur de la barre d'état ne change pas sur iPhone X sur iOS 11 ou iPhone XS iOS 12 lors d'un appel. Tout ce qui change, c'est l'icône de l'heure, qui devient verte dans les deux cas. Voici un instantané:

entrez la description de l'image ici


5
Les encarts de zone de sécurité contiendront la hauteur de la barre d'état, si elle est visible, sur d'autres appareils. Vérifier si c'est 0 ne vous dira que si la barre d'état est visible, pas si l'appareil est un iPhone X.
IMcD23

3
"Cela pourrait casser dans l' iPhone Xs ou l'iPhone 11 ", a déclaré Cook.
Itachi

11
Je me suis un peu adapté et j'utilise if _window.safeAreaInsets != UIEdgeInsets.zeropour permettre l'orientation de n'importe quel appareil
Fraser

2
Si vous ne voulez pas utiliser .top, safeAreaInsets.bottomsera 34 sur iPhone X et 0 sur d'autres appareils
blwinters

7
Avertissement: ne l'utilisez pas, cela casse sur iOS 12. Il n'est pas non plus documenté ce que UIWindow devrait faire dans ce cas. openradar.appspot.com/42372793
steipete

74

Vous devez effectuer différentes détections de l'iPhone X en fonction des besoins réels.

pour gérer le top (barre d'état, barre de navigation), 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
}

pour gérer l'indicateur d'accueil inférieur (barre de tabulation), 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
}

pour la taille des arrière-plans, les fonctionnalités plein écran, etc.

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

Remarque: éventuellement mélanger avec UIDevice.current.userInterfaceIdiom == .phone
Note: cette méthode nécessite d'avoir un storyboard LaunchScreen ou des LaunchImages appropriés

pour le taux d'arrière-plan, les fonctions de défilement, 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
}

Remarque: cette méthode nécessite d'avoir un storyboard LaunchScreen ou des LaunchImages appropriés

pour l'analyse, les statistiques, le suivi, etc.

Obtenez l'identifiant de la machine et comparez-le aux valeurs documentées:

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

Pour inclure le simulateur en tant qu'iPhone X valide dans vos analyses:

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

Pour inclure l'iPhone XS, XS Max et XR, recherchez simplement les modèles commençant par «iPhone11»:

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

pour le support 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
}

J'espérais que return LAContext().biometryType == .typeFaceIDcela fonctionnerait même si l'utilisateur avait refusé canEvaluatePolicy, mais cela ne fonctionne pas pour moi, il revient toujours.none
Jeremy

Eh bien @Jeremy, c'est un comportement documenté, conséquence de la politique de confidentialité d'Apple. C'est pourquoi le commentaire au dessus de la méthode.
Cœur

Ah, j'ai mal interprété votre commentaire. Je pensais que vous vouliez dire que l'utilisation de canEvaluatePolicy pouvait échouer, alors utilisez plutôt ce qui suit. Je trouve un peu étrange que vous soyez autorisé à vérifier si l'appareil a Face ID jusqu'à ce que l'utilisateur réponde à la bascule, puis vous ne pouvez même plus vérifier. Comment suis-je censé fournir un message d'erreur utile pour dire d'aller dans Paramètres et de basculer Face ID?
Jeremy

@Jeremy Je ne possède pas d'iPhone X, donc je ne sais pas. Peut-être que vous pourriez utiliser la détection de modèle ci-dessus ( model == "iPhone10,3" || model == "iPhone10,6"), et si canUseFaceIDrenvoie faux, cela signifie qu'il a été refusé par l'utilisateur.
Cœur

1
@MateoOlaya Rien dans ma réponse ne serait rejeté par Apple: vous pouvez tout utiliser.
Cœur

43

Vous pouvez faire comme cela pour détecter un appareil iPhone X selon sa dimension.

Rapide

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

Objectif c

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

entrez la description de l'image ici

Mais ,

Ce n'est pas un moyen suffisant. Et si Apple annonçait le prochain iPhone avec la même dimension que l'iPhone X. La meilleure façon est donc d'utiliser la chaîne Hardware pour détecter l'appareil.

Pour les appareils plus récents, la chaîne matérielle est la suivante.

iPhone 8 - iPhone10,1 ou iPhone 10,4

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

iPhone X - iPhone10,3 ou iPhone10,6


2
Vous devez utiliser à la [UIDevice currentDevice]place de[[UIDevice alloc] init]
S. Matsepura

le seul problème avec la chaîne matérielle est qu'elle ne fonctionne pas sur le simulateur
quemeful

39

Vérifiez le modèle de l'appareil / le nom de la machine , N'utilisez PAS directement le nombre de points / pixels dans votre code, c'est un code dur et n'a pas de sens pour le matériel de l'appareil, le modèle de l'appareil est le seul identifiant unique pour un type d'appareil à faire correspondre .

#import <sys/utsname.h>

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

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

Résultat:

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

Se référer à cette réponse .

Implémentation complète du code:

#import <sys/utsname.h>

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

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

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

    return strModelID;
}

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

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

BOOL IsNotchiPhone(void)
{
    NSArray<NSString *> *notchiModels = @[
        @"iPhone10,3", @"iPhone10,6", // iPhone X
        @"iPhone11,2", @"iPhone11,4", @"iPhone11,6", // iPhone XS (Max)
        @"iPhone11,8", // iPhone XR
        @"iPhone12,1", @"iPhone12,3", @"iPhone12,5", // iPhone 11 (Pro (Max))
    ];

    return [notchiModels containsObject:GetDeviceModel()];
}

1
Excellente réponse car elle gère correctement le simulateur. Veuillez ajouter la ligne #import à la section "code complet". J'ai raté cela (copier / coller) lors de ma première tentative.
mpoisot

1
c'est ma méthode préférée. Reportez-vous à ce wiki pour une liste complète des chaînes de modèle de périphérique. En guise de commentaire secondaire, @ "iphone10,3" pourrait également être considéré comme du code dur.
YvesLeBorg

1
@YvesLeBorg Oui, c'est vraiment une question controversée critique. La chaîne du modèle matériel a un identifiant unique que les points d'écran pour l'appareil, je pense. Généralement, il est utilisé pour les statistiques de données.
Itachi

26
#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)

définir IS_IPHONE_X (limites 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"]

Remarque: - Attention, cela fonctionne très bien uniquement pour l'orientation portrait


2
Attention, cela fonctionne très bien uniquement pour l'orientation portrait
CFIFok

1
Merci pour cela. Fonctionne bien. En mode paysage, vous devez ajuster ces nombres. Le nombre magique d'iPhoneX en mode paysage est
375.0

Il y a quelques iPhone Plus / Max / Pro nativeScaleavec 3.0, non?
Itachi

25

Après avoir regardé toutes les réponses, voici ce que j'ai fini par faire:

Solution (compatible 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
    }
}

Utilisation

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

Remarque

Avant Swift 4.1, vous pouvez vérifier si l'application fonctionne sur un simulateur comme celui-ci:

TARGET_OS_SIMULATOR != 0

À partir de Swift 4.1 et versions ultérieures, vous pouvez vérifier si l'application s'exécute sur un simulateur à l'aide de la condition de plate-forme d'environnement cible :

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

(l'ancienne méthode fonctionnera toujours, mais cette nouvelle méthode est plus à l'épreuve du temps)


est la pomme ira bien avec ça?
Surjeet Rajput

@ commando24 Oui, je ne vois aucune raison pour laquelle ils rejettent l'application à cause de ce code.
Cloud9999Strife

18

Toutes ces réponses basées sur les dimensions sont susceptibles d'un comportement incorrect sur les futurs appareils. Ils fonctionneront aujourd'hui, mais que se passera-t-il s'il y a un iPhone l'année prochaine de la même taille mais avec l'appareil photo, etc. sous la vitre, donc il n'y a pas de "cran?" Si la seule option est de mettre à jour l'application, c'est une mauvaise solution pour vous et vos clients.

Vous pouvez également vérifier la chaîne du modèle matériel comme "iPhone10,1", mais cela pose problème car parfois Apple publie des numéros de modèle différents pour différents opérateurs à travers le monde.

L'approche correcte consiste à repenser la disposition supérieure ou à résoudre les problèmes que vous rencontrez avec la hauteur de la barre de navigation personnalisée (c'est ce sur quoi je me concentrerais). Mais, si vous décidez de ne faire aucune de ces choses, sachez que tout ce que vous faites est un hack pour que cela fonctionne aujourd'hui , et vous devrez le corriger à un moment donné, peut-être plusieurs fois, pour garder les hacks travail.


1
Droite. Affiner l'hypothèse que le nombre X sera toujours A à un que le nombre X sera toujours A à moins que la condition Y quand il sera B ne soit en train de creuser plus profondément. Taille basée sur la zone de sécurité nommée par Apple, pas en la devinant.
Tommy

2
Je m'inquiéterai pour le prochain iPhone quand il sera disponible. Je veux que mon application fonctionne AUJOURD'HUI.
Vahid Amiri

13

Réponse de SWIFT 4+

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

Remarque: besoin d'un véritable appareil pour le test

Référence

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

Pour la méthode 1, vous pouvez supprimer la propriété "var window" à l'extérieur du func et juste une constante "let" à l'intérieur (tapez UIWindow, c'est-à-dire non facultative). J'aime cette réponse car au démarrage, self.view.window peut être nul et UIApplication.shared.keyWindow peut également être nul, tandis que la création d'une UIWindow de cette manière fonctionne à chaque fois.
Rolleric

11

SWIFT 4/5 réutilisable l' extension avec l' iPhone 11 support

    public extension UIDevice {

    public enum `Type` {
        case iPad
        case iPhone_unknown
        case iPhone_5_5S_5C
        case iPhone_6_6S_7_8
        case iPhone_6_6S_7_8_PLUS
        case iPhone_X_Xs
        case iPhone_Xs_11_Pro_Max
        case iPhone_Xr_11
        case iPhone_11_Pro
    }

    public var hasHomeButton: Bool {
        switch type {
        case .iPhone_X_Xs, .iPhone_Xr_11, .iPhone_Xs_11_Pro_Max, .iPhone_11_Pro:
            return false
        default:
            return true
        }
    }

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

2
bonne extension, mais le plus utile ici estUIDevice.current.hasHomeButton
WINSergey

1
@ale_stro est-il bon d'utiliser userInterfaceIdiom pour déterminer les appareils pour l'application universelle? la plupart des gens ne le recommandent pas. y a-t-il un mal à l'utiliser?
shaqir saiyed

10

Oui c'est possible. Téléchargez l' extension UIDevice-Hardware (ou installez-la via CocoaPod 'UIDevice-Hardware') puis utilisez:

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

Notez que cela ne fonctionnera pas dans le simulateur, uniquement sur le périphérique réel.


Tous les codes d'appareil ici: iphonesoft.fr/2016/10/31/… Exemple: iPhone X: iPhone10,5 et iPhone10,6
Medhi

Les chaînes Hardware de wikipedia ont dit "iPhone10,3 et iPhone10,6". @Medhi
Itachi

@Medhi, vous pouvez utiliser ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]dans Simulator pour obtenir les valeurs réelles de Xcode.
Cœur

9

Selon la réponse de @ saswanb, il s'agit d'une version Swift 4:

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

La barre d'état est également considérée en dehors de la zone de sécurité! donc cela retournera des faux positifs! Il doit être supérieur à 20 points (hauteur de la barre d'état). Cela est également vrai si l'appareil est un iPhone Xs, R ou Xs Max.
MQoder

le code fonctionne très bien, mais attention: keyWindowest niljusqu'à ce que le contrôleur principal de vue a appeléviewDidAppear
Casey

9

Je sais que ce n'est qu'un Swift solution , mais cela pourrait aider quelqu'un.

J'ai globals.swiftdans chaque projet et l'une des choses que j'ajoute toujours est DeviceTypede détecter facilement l'appareil de l'utilisateur:

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

Ensuite pour l'utiliser:

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 vous utilisez LaunchImagedans votre projet, assurez-vous d'ajouter des images pour tous les appareils pris en charge (comme XS Max, XR) car UIScreen.main.boundsils ne retourneront pas la valeur appropriée sans ceux-ci.


1
Un nouvel ami de Swift a demandé comment l'utiliser, juste au cas où quelqu'un d'autre ne le sait pas… if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling

5

Toutes les réponses qui utilisent le heightsont seulement la moitié de l'histoire pour une raison. Si vous allez vérifier comme ça quand l'orientation de l'appareil est landscapeLeftou landscapeRightla vérification échouera, car le heightest échangé avec lewidth .

C'est pourquoi ma solution ressemble à ceci dans 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
    }
}

Utilisez simplement nativeBounds à la place
Leo Dabus

4

Vous ne devez pas supposer que le seul appareil qu'Apple publie avec une hauteur UINavigationBar différente sera l'iPhone X. Essayez de résoudre ce problème en utilisant une solution plus générique. Si vous souhaitez que la barre soit toujours 20px plus grande que sa hauteur par défaut, votre code doit ajouter 20px à la hauteur de la barre, au lieu de la définir sur 64px (44px + 20px).


Alors, quelle autre solution avez-vous à proposer?
Stephane Mathis

@xaphod, il y a de meilleures réponses maintenant.
Cœur

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

struct DeviceType {
    static let iPhone4orLess = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength < 568.0
    static let iPhone5orSE = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 568.0
    static let iPhone678 = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 667.0
    static let iPhone678p = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 736.0
    static let iPhoneX = UIDevice.current.userInterfaceIdiom == .phone && ScreenSize.maxLength == 812.0

    static let IS_IPAD              = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1024.0
    static let IS_IPAD_PRO          = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1366.0
}

4

Swift 3 + 4:

sans avoir besoin d'une valeur de pixel de taille d'appareil

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

Exemple:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}

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

2
il vous renverra 812 si vous téléchargez l'image par défaut pour iPhone X. D'ici là, je pense qu'il vous rendra la taille de l'iPhone 7, pas sûr cependant ...
Fahim Parkar

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

1
Meilleure réponse! Sans avoir besoin d'une valeur de pixel de taille d'appareil.
Peter Kreinz

3

Habituellement, le programmeur en a besoin pour contraindre en haut ou en bas, donc ces méthodes peuvent aider

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
}

Pour avant iPhone X, ces méthodes renvoient: 0

Pour iPhone X: 44 et 34 en conséquence

Ensuite, ajoutez simplement ces extras aux contraintes supérieures ou inférieures


3

Pour ceux qui obtiennent 2001px au lieu de 2436px pour la hauteur des limites natives (comme moi), c'est parce que vous avez construit votre application avec un SDK plus ancien, avant iOS 11 (Xcode 8 au lieu de Xcode 9). Avec un SDK plus ancien, iOS affichera les applications "en boîte noire" sur l'iPhone X au lieu d'étendre l'écran bord à bord, au-delà du "cran du capteur" supérieur. Cela réduit la taille de l'écran, c'est pourquoi cette propriété renvoie 2001 au lieu de 2436.

La solution la plus simple consiste à simplement vérifier les deux tailles si vous êtes uniquement intéressé par la détection des appareils. J'ai utilisé cette méthode pour détecter FaceID lors de la construction avec un SDK Xcode plus ancien qui n'a pas la valeur ENUM spécifiant le type biométrique. Dans cette situation, la détection de l'appareil en utilisant la hauteur de l'écran semblait être le meilleur moyen de savoir si l'appareil avait FaceID vs TouchID sans avoir à mettre à jour Xcode.


3

N'utilisez PAS la taille des pixels de l'écran comme d'autres solutions l'ont suggéré, c'est mauvais car cela peut entraîner des faux positifs pour les futurs appareils; ne fonctionnera pas si UIWindow n'a pas encore été rendu (AppDelegate), ne fonctionnera pas dans les applications de paysage et peut échouer sur le simulateur si l'échelle est définie.

J'ai, à la place, créé une macro à cet effet, elle est très facile à utiliser et s'appuie sur des indicateurs matériels pour éviter les problèmes susmentionnés.

Modifier: mis à jour pour prendre en charge iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Utiliser:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Ouais, vraiment.


Macro:

Copiez-collez cela n'importe où, je préfère le bas de mon fichier .h aprè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]);\
})()\
)

La seule raison pour laquelle je peux penser pour détecter iPhoneX est d'éviter l'encoche en haut de l'écran; si c'est le cas, vous pouvez vérifier le coffre-fort pour détecter la taille de ladite encoche. Assurez-vous simplement de le mesurer après le chargement de UIWindow, donc pas pendant viewDidLoad mais un cycle de thread après:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw

2

J'ai développé vos réponses sur les autres et fait une extension rapide sur UIDevice. J'aime les énumérations rapides et "tout en ordre" et atomisé. J'ai créé une solution qui fonctionne à la fois sur l'appareil et le simulateur.

Avantages: - interface simple, utilisation par exemple UIDevice.current.isIPhoneX -UIDeviceModelType enum vous donne la possibilité d'étendre facilement les fonctionnalités et les constantes spécifiques au modèle que vous souhaitez utiliser dans votre application, par exemple cornerRadius

Inconvénient: - c'est une solution spécifique au modèle, pas une résolution spécifique - par exemple, si Apple produira un autre modèle avec les mêmes spécifications, cela ne fonctionnera pas correctement et vous devez ajouter un autre modèle pour que cela fonctionne => vous devez mettre à jour votre app.

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

Au lieu d'utiliser Mirror, il sera plus rapide à utiliser sysctlbynamecomme cela est fait dans la réponse Cloud9999Strife (et dans ma réponse aussi).
Cœur

2

Je compte sur la hauteur du cadre de la barre d'état pour détecter s'il s'agit d'un iPhone X:

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

C'est pour l'application un portrait. Vous pouvez également vérifier la taille en fonction de l'orientation de l'appareil. De plus, sur d'autres iPhones, la barre d'état peut être masquée, la hauteur du cadre est donc 0. Sur iPhone X, la barre d'état n'est jamais masquée.


Vous pouvez masquer la barre d'état de l'iPhoneX controlleravec ceci: - (BOOL)prefersStatusBarHidden { return YES; } la hauteur de la barre d'état est alors de 0.
无 夜 之 星辰

@ 无 夜 之 星辰 Je vérifie cela au démarrage dans l'AppDelegate.
Tiois

2

J'utilisais le code de Peter Kreinz (car il était propre et a fait ce que je avais besoin) mais je l' ai réalisé fonctionne lorsque l'appareil est en mode portrait (depuis padding haut sera sur le dessus, évidemment) Je créé une extension pour gérer toutes les orientations avec ses rembourrages respectifs, sans relayer sur la taille de l'écran:

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

Et sur votre site d'appel, vous venez de:

let res = UIDevice.current.isIphoneX

2

Alternativement, vous pouvez consulter le module « DeviceKit ». Une fois installé, tout ce que vous devez faire pour vérifier l'appareil est:

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

2

Nov 2019:

Voici ce que j'utilise dans tous mes projets de production. Notez que cet essentiel est assez long.

  1. Cela n'utilise pas de calculs de largeur ou de hauteur, mais plutôt:
  2. Il vérifie le modèle de chaîne de périphérique.
  3. N'a pas le risque de voir votre build rejeté par Apple en raison de l'utilisation d'API privées / non documentées.
  4. Fonctionne avec des simulateurs 💯

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

Utilisation: laissez l'encart: CGFloat = DeviceUtility.isIphoneXType? 50,0: 40,0


Fonctionne parfaitement. Merci. Je l'utilise dans un projet SwiftUI.
LondonGuy

1

J'ai dû résoudre le même problème récemment. Et bien que cette question ait une réponse définitive («Non»), cela peut aider ceux qui ont besoin d'un comportement de mise en page spécifique à l'iPhone X.

Je n'étais pas vraiment intéressé à savoir si l'appareil était iPhone X. Je voulais savoir si l'appareil avait un écran cranté.

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
}

Vous pouvez également écrire un hasOnScreenHomeIndicator variable dans le même sens (bien que vérifier la zone de sécurité inférieure, peut-être?).

Ce qui précède utilise mon extension UIViewpour un accès pratique aux encarts de zone de sécurité sur iOS 10 et versions antérieures.

@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

Dans Portrait uniquement, j'utilise la largeur et la hauteur du cadre de la vue pour vérifier:

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

}

Les dimensions de l'écran portrait sont répertoriées ici

entrez la description de l'image ici


0

Il y a plusieurs raisons de vouloir savoir ce qu'est l'appareil.

  1. Vous pouvez vérifier la hauteur (et la largeur) de l'appareil. Ceci est utile pour la mise en page, mais vous ne voulez généralement pas le faire si vous voulez connaître le périphérique exact.

  2. À des fins de mise en page, vous pouvez également utiliser UIView.safeAreaInsets.

  3. Si vous souhaitez afficher le nom du périphérique, par exemple, à inclure dans un e-mail à des fins de diagnostic, après avoir récupéré le modèle de périphérique à l'aide sysctl (), vous pouvez utiliser l'équivalent de celui-ci pour comprendre le nom:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.