web-dev-qa-db-fra.com

Détecter si l'appareil est l'iPhone X

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

Est-ce que quelqu'un sait déjà fiable détecter par programme (en Objective-C) si une application est exécutée sur iPhone X?

MODIFIER:

Bien sûr, il est possible de vérifier la taille de l'écran, mais je me demande s'il existe une méthode "intégrée" comme TARGET_OS_IPHONE pour 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 est un duplicata de la question liée. Bien sûr, il existe des méthodes pour "mesurer" différentes propriétés du périphérique actuel et pour utiliser les résultats afin de déterminer le périphérique utilisé. Cependant, ce n’était pas le propos de ma question, j’ai essayé de le souligner lors de ma première édition. 

La question est la suivante: "Est-il possible de détecter directement si le périphérique actuel est un iPhone X (par exemple, grâce à une fonctionnalité du 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éthodes directes. Les mesures sont la voie à suivre". 

216
Andrei Herford

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

et

La hauteur de l'iPhone X est de 2436 px

À partir de Tailles et résolutions de l'écran du périphérique :

 enter image description here

Depuis Dimensions et orientations de l'écran du périphérique :

 enter image description here

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

        case 2688:
            print("iPhone XS Max")

        case 1792:
            print("iPhone XR")

        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:
            printf("iPhone X, XS");
            break;

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

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

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

Xamarin.iOS:

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

Basé sur votre question comme suit:

Ou utilisez screenSize.height comme float 812.0f pas 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 vous reporter à la page suivante dans iOS Human Interface Guidelines:

Rapide:

Détecter avec topNotch:

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

    return false
}

Objectif c:

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

    return  NO;
}

METTRE À JOUR:

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

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 créer différentes vues et commandes en fonction du type d'appareil actuel.

Autrement dit, cette propriété est simplement utilisée pour identifier le style de vue de l'application en cours d'exécution. Toutefois, l'application iPhone (et non la version universelle) peut être installée sur un périphérique iPad via App store. Dans ce cas, la variable userInterfaceIdiom renverra également la variable UIUserInterfaceIdiomPhone.

La bonne façon consiste à obtenir le nom de la machine via uname. Vérifiez les points suivants pour plus de détails:

306
Anbu.Karthik

Une 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 sécurité des zones de sécurité gauche et droite si vous êtes en orientation paysage.

Edit: _window est la UIWindow de 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, accédez à Matériel, Basculer la barre d’état en cours de communication. Cela me montre que la hauteur de la barre d'état ne change pas sur iPhone X sous iOS 11 ou iPhone XS iOS 12 lors d'un appel. Tout ce qui change, c’est l’icône de temps, qui obtient un fond vert, dans les deux cas. Voici un cliché:

 enter image description here

92
saswanb

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

pour traiter avec le top notch (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 traiter l'indicateur d'accueil du bas (tabbar), 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
}

Note: éventuellement le mélanger avec UIDevice.current.userInterfaceIdiom == .phone
Remarque: cette méthode nécessite un story-board LaunchScreen ou des images LaunchImages appropriées.

pour le rapport des fonds, 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 story-board LaunchScreen ou des images LaunchImages appropriées.

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 les 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
}
52
Cœur

Vous pouvez faire comme ceci pour détecter le périphérique iPhone X en fonction de la 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     
}

 enter image description here

Mais,

Ce n'est pas suffisant. Et si Apple annonçait le prochain iPhone avec la même dimension que l'iPhone X. Le meilleur moyen consiste donc à utiliser une chaîne matérielle pour détecter l'appareil.

Pour les périphériques 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

41
Jaydeep

Vérifiez le modèle de l'appareil/le nom de l'ordinateur , n'utilisez pas directement le nombre de points/pixels dans votre code, il s'agit de hard code et n'a pas de sens pour le matériel de l'appareil.

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

Reportez-vous à 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)
{
    NSString *strModelID = GetDeviceModel();

    return [strModelID isEqualToString:@"iPhone10,3"] || [strModelID isEqualToString:@"iPhone10,6"] || // iPhone X
           [strModelID isEqualToString:@"iPhone11,2"] || [strModelID isEqualToString:@"iPhone11,4"] || [strModelID isEqualToString:@"iPhone11,6"] || // iPhone XS (Max)
           [strModelID isEqualToString:@"iPhone11,8"]; // iPhone XR
}
36
Itachi
#define IS_IPHONE        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS  (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_IPHONE_X      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)

définir IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen]]] .size.height == 812.0)

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

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

Remarque: - Faites attention, cela ne fonctionne bien que pour l'orientation portrait

25
Jagveer Singh

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

Pre Swift 4.1, vous pouvez vérifier si l'application fonctionne sur un simulateur comme ceci:

TARGET_OS_SIMULATOR != 0

À partir de Swift 4.1, vous pouvez vérifier si l'application est exécutée sur un simulateur à l'aide de la condition de la plate-forme de l'environnement cible

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

(l'ancienne méthode fonctionnera toujours, mais cette nouvelle méthode est plus durable)

23
Cloud9999Strife

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 passe-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, de sorte qu'il n'y ait pas de "encoche"? Si la seule option est de mettre à jour l'application, c'est une mauvaise solution pour vous et vos clients.

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

La bonne approche consiste à repenser la mise en page supérieure ou à résoudre les problèmes que vous rencontrez avec la hauteur de 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, réalisez que quoi que vous fassiez, c'est un bidouillage pour que cela fonctionneaujourd'hui, et vous devrez le corriger à un moment donné, peut-être plusieurs fois, pour que les bidouilles fonctionnent.

17
clarus

Réponse rapide 4+

iPhone X, XR, XS, XSMAX: ​​

_ {Remarque: besoin d'un périphérique réel 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")
        default:
            break
}

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

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, mais uniquement sur le périphérique réel.

10
Hendrik

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

J'ai globals.Swift dans chaque projet et l'une des choses que j'ajoute toujours est DeviceType pour détecter facilement le périphérique 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.maxLength == 896.0
  static var hasNotch: Bool {
    return iPhoneX || iPhoneXRMax
  }
}

Puis 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 LaunchImage dans votre projet, veillez à ajouter des images pour tous les périphériques pris en charge (tels que XS Max, XR), car UIScreen.main.bounds ne renverra pas la valeur appropriée sans ces éléments.

7
budidino

Swift 4 réutilisable extension

    public extension UIDevice {

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

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

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

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

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

Toutes les réponses qui utilisent la variable height ne représentent qu'une partie de l'histoire pour une raison. Si vous voulez vérifier de la sorte lorsque l'orientation du périphérique est landscapeLeft ou landscapeRight, la vérification échouera car la height est remplacée par la width.

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

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

4
IMcD23

Swift 3 + 4:

sans besoin d'aucune valeur de pixel de taille de périphérique

//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
}
4
Peter Kreinz
struct ScreenSize {
    static let width = UIScreen.main.bounds.size.width
    static let height = UIScreen.main.bounds.size.height
    static let maxLength = max(ScreenSize.width, ScreenSize.height)
    static let minLength = min(ScreenSize.width, ScreenSize.height)
    static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
}

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

    static let IS_IPAD              = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1024.0
    static let IS_IPAD_PRO          = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1366.0
}
4
Kiran Sarvaiya
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
3
alexander.pan

En général, le programmeur en a besoin pour se limiter en haut ou en bas. Ces méthodes peuvent donc vous 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
}

Avant iPhone X, ces méthodes sont renvoyées: 0

Pour iPhone X: 44 et 34 en conséquence

Ajoutez simplement ces extras aux contraintes du haut ou du bas

3
Andrey

Pour ceux qui obtiennent 2001px au lieu de 2436px pour la hauteur de limite native (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 affiche les applications «en boîte noire» sur l'iPhone X au lieu d'étendre l'écran bord à bord, au-delà de la «coche du capteur» supérieure. Cela réduit la taille de l'écran, raison pour laquelle 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 de périphérique. J'ai utilisé cette méthode pour détecter FaceID lors de la compilation avec un SDK Xcode plus ancien dont la valeur ENUM ne spécifiait pas le type biométrique. Dans cette situation, la détection de périphérique à l'aide de la hauteur de l'écran semblait être le meilleur moyen de savoir si le périphérique possédait FaceID vs TouchID sans avoir à mettre à jour Xcode.

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

J'ai expliqué les réponses de vos interlocuteurs et fait l'extension de Swift sur UIDevice. J'aime les énormes Swift et "tout va bien" et atomisé. J'ai créé une solution qui fonctionne à la fois sur un appareil et sur un simulateur. 

Avantages: - interface simple, utilisation p. Ex. UIDevice.current.isIPhoneX- UIDeviceModelType enum vous permet 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 devrez ajouter un autre modèle pour que cela fonctionne => vous devez mettre à jour votre application.

extension UIDevice {

    enum UIDeviceModelType : Equatable {

        ///iPhoneX
        case iPhoneX

        ///Other models
        case other(model: String)

        static func type(from model: String) -> UIDeviceModelType {
            switch model {
            case "iPhone10,3", "iPhone10,6":
                return .iPhoneX
            default:
                return .other(model: model)
            }
        }

        static func ==(lhs: UIDeviceModelType, rhs: UIDeviceModelType) -> Bool {
            switch (lhs, rhs) {
            case (.iPhoneX, .iPhoneX):
                return true
            case (.other(let modelOne), .other(let modelTwo)):
                return modelOne == modelTwo
            default:
                return false
            }
        }
    }

    var simulatorModel: String? {
        guard TARGET_OS_SIMULATOR != 0 else {
            return nil
        }

        return ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"]
    }

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

        return model
    }

    var modelType: UIDeviceModelType {
        let model = self.simulatorModel ?? self.hardwareModel
        return UIDeviceModelType.type(from: model)
    }

    var isIPhoneX: Bool {
        return modelType == .iPhoneX
    }
}
2
deathhorse

N'utilisez PAS la taille de pixel de l'écran comme le suggèrent d'autres solutions. Cela est grave, car cela peut entraîner de 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.

Au lieu de cela, j'ai créé une macro à cet effet, très facile à utiliser et reposant 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:

Il suffit de copier-coller ceci 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]);\
})()\
)
2
Albert Renshaw

Je me fie à 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
}

Ceci est pour l'application un portrait. Vous pouvez également vérifier la taille en fonction de l'orientation du périphérique. 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.

2
Tiois

J'utilisais le code de Peter Kreinz (car il était propre et a fait ce dont j'avais besoin), mais je me suis rendu compte que cela fonctionnait au moment où l'appareil est en mode portrait (puisque le remplissage supérieur sera au-dessus, évidemment) 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:

let res = UIDevice.current.isIphoneX
2
rgkobashi

Vous pouvez également consulter ' DeviceKit ' pod . Une fois installé, il vous suffit de vérifier le périphérique:

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

J'ai dû résoudre le même problème récemment. Et bien que l'on réponde définitivement à cette question ("Non"), cela pourrait aider les autres personnes qui ont besoin d'un comportement de mise en page spécifique pour iPhone X 

L'iPhone X ne m'intéressait pas vraiment. L'appareil possédait-il un écran à encoches?.

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 une variable hasOnScreenHomeIndicator 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 sur UIView pour un accès pratique aux encarts de zone de sécurité sur iOS 10 et les 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
simeon

Il y a plusieurs raisons de vouloir savoir quel est le périphérique.

  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. Pour des raisons 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 courrier électronique à des fins de diagnostic, après avoir récupéré le modèle de périphérique à l'aide de sysctl (), vous pouvez utiliser l'équivalent pour indiquer le nom:

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

Je pense que Apple ne veut pas que nous vérifions manuellement si le périphérique a un "cran" ou un "indicateur de domicile" mais le code qui fonctionne est le suivant:

-(BOOL)hasTopNotch{

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

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

        return max_safe_area_inset >= 44.0;

    }

    return  NO;

}

-(BOOL)hasHomeIndicator{

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

        int iNumberSafeInsetsEqualZero = 0;

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

        return iNumberSafeInsetsEqualZero <= 2;

    }

    return  NO;

}

Certains des autres messages ne fonctionnent pas. Par exemple, l'iPhone 6S avec "barre d'état en cours d'appel" (barre verte) en mode portrait comporte un grand cadre sécurisé. Avec mon code, tous les cas sont pris en compte ( même si l'appareil démarre en mode portrait ou paysage )

0
BIOS-K

Le moyen le plus simple et le plus simple de détecter si l'appareil est un iPhone X est,

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

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

Et l'identifiant est soit "iPhone10,3" ou "iPhone10,6" pour iPhone X.

0
satheeshwaran

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 listées ici

 enter image description here

0
Lance Samaria

Avec la sortie d'iOS 12, des appareils comme l'iPhone X peuvent entrer dans cette catégorie. 

extension UIDevice {
    var isPortrait: Bool {

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

  }

   var isDeviceWith_XShape : Bool {

    if self.userInterfaceIdiom == .phone {

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

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

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


    }
    else
    {
        return false
    }

}`
0
dev_shanghai

Pour une solution rapide, j'aime ceci:

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

Pour détecter l'un des périphériques à l'aide de méthodes simples. comme ci-dessous,

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

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

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

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

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

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

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

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

et appelle comme ça,

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