Rileva se il dispositivo è iPhone X.


262

La mia app iOS utilizza un'altezza personalizzata per il UINavigationBarche porta ad alcuni problemi sul nuovo iPhone X.

Qualcuno sa già come rilevare in modo affidabile a livello di codice (in Objective-C) se un'app è in esecuzione su iPhone X?

MODIFICARE:

Ovviamente è possibile controllare le dimensioni dello schermo, tuttavia mi chiedo se esiste un metodo "build in" come TARGET_OS_IPHONErilevare iOS ...

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

MODIFICA 2:

Non penso che la mia domanda sia un duplicato della domanda collegata. Naturalmente, ci sono metodi per "misurare" diverse proprietà del dispositivo corrente e usare i risultati per decidere quale dispositivo viene utilizzato. Tuttavia, questo non era il vero punto della mia domanda, come ho cercato di enfatizzare nella mia prima modifica.

La vera domanda è: "È possibile rilevare direttamente se il dispositivo corrente è un iPhone X (ad es. Con una funzione SDK) o devo usare misure indirette" ?

Dalle risposte fornite finora, suppongo che la risposta sia "No, non esistono metodi diretti. Le misure sono la strada da percorrere".


iPhone X ha una risoluzione dello schermo diversa dalle altre.
El Tomato,

2
Sì, come ho già detto nella mia modifica, è possibile controllare le dimensioni dello schermo. Comunque la domanda è, se esiste un metodo "diretto" per interrogare il tipo di dispositivo piuttosto che misurazioni "indirette" ...
Andrei Herford,

3
L'autore vuole solo ottenere il tipo di dispositivo, non la risoluzione dello schermo. Perché non controllare direttamente il nome della macchina? @lubilis ha ragione.
Itachi,

2
perché non usi semplicemente le guide delle aree sicure come è raccomandato da Apple?
Holex,

4
IMPORTANTE, futuri sviluppatori: non rilevare questo utilizzando l'altezza dello schermo come suggeriscono le migliori soluzioni attuali, è male in quanto può provocare falsi positivi per i dispositivi futuri; non funzionerà se UIWindow non ha ancora eseguito il rendering (come nelle funzioni init AppDelegate), non funzionerà nelle app orizzontali e potrebbe non riuscire nel simulatore se è impostata la scala. MAI usare numeri magici per cose come questa! Puoi controllare i flag hardware per garantire il successo come ho fatto qui: stackoverflow.com/a/51511947/2057171
Albert Renshaw,

Risposte:


384

Sulla base della tua domanda, la risposta è no. Non ci sono metodi diretti. Per ulteriori informazioni è possibile ottenere le informazioni qui:

e

L'altezza di iPhone X è di 2436 px

Da dimensioni e risoluzioni dello schermo del dispositivo :

inserisci qui la descrizione dell'immagine

Dalle dimensioni e dagli orientamenti dello schermo del dispositivo :

inserisci qui la descrizione dell'immagine

Swift 3 e versioni successive :

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

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

Sulla base della tua domanda come segue:

Oppure usa screenSize.heightcome float 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");
    }

Per ulteriori informazioni è possibile fare riferimento alla seguente pagina nelle Linee guida per l'interfaccia umana iOS:

Veloce :

Rileva con topNotch:

Se qualcuno sta pensando di utilizzare notch per rilevare iPhoneX, ricorda che su "landscape" è lo stesso per tutti gli iPhone.

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
}

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

AGGIORNAMENTO :

Non utilizzare la userInterfaceIdiomproprietà per identificare il tipo di dispositivo, come spiegato nella documentazione per userInterfaceIdiom :

Per le applicazioni universali, è possibile utilizzare questa proprietà per personalizzare il comportamento dell'applicazione per un tipo specifico di dispositivo. Ad esempio, i dispositivi iPhone e iPad hanno schermi di dimensioni diverse, quindi potresti voler creare viste e controlli diversi in base al tipo di dispositivo corrente.

Cioè, questa proprietà viene utilizzata solo per identificare lo stile di visualizzazione dell'app in esecuzione. Tuttavia, l'app per iPhone (non quella universale) potrebbe essere installata sul dispositivo iPad tramite App store, in tal caso userInterfaceIdiomrestituirà UIUserInterfaceIdiomPhoneanche.

Il modo giusto è ottenere il nome della macchina tramite uname. Controllare quanto segue per i dettagli:


La risoluzione di iPhone X è di 2436 x 1125 pixel secondo: iphonesoft.fr/2017/09/12/…
Medhi

1
@Medhi - la risoluzione di iphone X è - 1125 x 2436 pixel (densità pixel ~ 458 ppi)
Anbu.Karthik

14
NO! L'app per iPhone (non l'universo) potrebbe essere installata sul dispositivo iPad tramite App store, in tal casouserInterfaceIdiomrestituiràUIUserInterfaceIdiomPhoneanche. Questa risposta è sbagliata
Itachi,

1
@ThreeCoins, aggiorna la tua risposta per i dispositivi plus come suggerito da Leo Dabus. Funziona sul simulatore Plus ma non sul dispositivo.
Hiren Gujarati,

2
Ciò è negativo in quanto può comportare falsi positivi per i dispositivi futuri; non funzionerà se UIWindow non ha ancora eseguito il rendering (AppDelegate), non funzionerà nelle app orizzontali e può fallire nel simulatore se è impostata la scala. Puoi controllare i flag hardware per garantire il successo come ho fatto qui: stackoverflow.com/a/51511947/2057171
Albert Renshaw,

101

Un'altra possibilità, che funziona su iOS 11 e iOS 12 perché l'iPhone X è l'unico con una tacca in alto e un inserto di 44. Questo è ciò che sto davvero rilevando qui:

Objective-C:

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

Swift 4:

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

E, naturalmente, potrebbe essere necessario controllare gli inserti dell'area sicura sinistra e destra se si è in orientamento orizzontale.

Modifica: _window è la UIWindow dell'AppDelegate, dove questo controllo viene eseguito nell'applicazione didFinishLaunchingWithOptions.

Risposta aggiornata per iOS 12 per verificare se in alto> 24 anziché in alto> 0.

Modifica: nel simulatore è possibile accedere a Hardware, Attiva / Disattiva barra di stato in chiamata. Questo mi mostra che l'altezza della barra di stato non cambia su iPhone X su iOS 11 o iPhone XS iOS 12 quando si è impegnati in una chiamata. Tutto ciò che cambia è l'icona del tempo, che diventa uno sfondo verde, in entrambi i casi. Ecco uno scatto:

inserisci qui la descrizione dell'immagine


5
Gli inserti dell'area sicura conterranno l'altezza della barra di stato, se visibile, su altri dispositivi. Controllare se questo è 0 ti dirà solo se la barra di stato è visibile, non se il dispositivo è un iPhone X.
IMcD23

3
"Questo potrebbe rompersi in iPhone Xs o iPhone 11 ", ha dichiarato Cook.
Itachi,

11
Mi sono adattato un po 'e lo uso if _window.safeAreaInsets != UIEdgeInsets.zeroper consentire qualsiasi orientamento del dispositivo
Fraser,

2
Se non vuoi usarlo .top, safeAreaInsets.bottomsaranno 34 su iPhone X e 0 su altri dispositivi
blwinters

7
Avvertenza: non usare questo, si rompe su iOS 12. Inoltre non è documentato cosa dovrebbe fare UIWindow in questo caso. openradar.appspot.com/42372793
steipete,

73

Dovrai eseguire diversi rilevamenti di iPhone X a seconda della necessità effettiva.

per gestire il massimo (barra di stato, barra di navigazione), ecc.

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
}

per gestire l'indicatore home in basso (tabbar), ecc.

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
}

per dimensioni dello sfondo, funzioni a schermo intero, ecc.

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

Nota: eventualmente mescolarlo con UIDevice.current.userInterfaceIdiom == .phone
Nota: questo metodo richiede uno storyboard LaunchScreen o LaunchImages appropriati

per rapporto sfondi, funzioni di scorrimento, ecc.

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

Nota: questo metodo richiede uno storyboard LaunchScreen o LaunchImages appropriati

per analisi, statistiche, tracciamento, ecc.

Ottieni l'identificatore della macchina e confrontalo con i valori documentati:

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

Per includere il simulatore come un iPhone X valido nell'analisi:

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

Per includere iPhone XS, XS Max e XR, cerca semplicemente i modelli che iniziano con "iPhone11":

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

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

Speravo che return LAContext().biometryType == .typeFaceIDfunzionasse anche se l'utente avesse negato canEvaluatePolicy, ma non funziona per me, ritorna comunque.none
Jeremy,

Bene @Jeremy, è un comportamento documentato, conseguenza della politica sulla privacy di Apple. Ecco perché il commento sopra il metodo.
Cœur l'

Ah, ho frainteso il tuo commento. Pensavo che intendevi usare canEvaluatePolicy potrebbe fallire, quindi usa invece quanto segue. Trovo un po 'strano che ti sia permesso controllare se il dispositivo ha Face ID fino a quando l'utente non risponde al toggle e quindi non puoi nemmeno più controllare. Come posso fornire un utile messaggio di errore da dire per andare su Impostazioni e attivare Face ID?
Jeremy,

@Jeremy Non possiedo un iPhone X, quindi non lo so. Forse potresti utilizzare il rilevamento del modello sopra ( model == "iPhone10,3" || model == "iPhone10,6") e se canUseFaceIDrestituisce false, significa che è stato negato dall'utente.
Cœur il

1
@MateoOlaya Nulla nella mia risposta sarebbe respinto da Apple: puoi usarlo tutto.
Cœur,

42

Puoi fare così per rilevare il dispositivo iPhone X in base alle dimensioni.

veloce

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

Obiettivo - C

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

inserisci qui la descrizione dell'immagine

ma ,

Questo non è un modo sufficiente. E se Apple avesse annunciato il prossimo iPhone con la stessa dimensione di iPhone X. Quindi il modo migliore è usare la stringa hardware per rilevare il dispositivo.

Per i dispositivi più recenti, la stringa hardware è la seguente.

iPhone 8 - iPhone10,1 o iPhone 10,4

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

iPhone X - iPhone10,3 o iPhone10,6


2
Dovresti usare [UIDevice currentDevice]invece di[[UIDevice alloc] init]
S. Matsepura il

l'unico problema con la stringa hardware è che non funziona sul simulatore
quemeful

38

Controlla il modello del dispositivo / il nome della macchina , NON utilizzare il conteggio punti / pixel nel codice direttamente, è un codice fisso e privo di significato per l'hardware del dispositivo , il modello del dispositivo è l'unico identificatore univoco per un tipo di dispositivo da abbinare .

#import <sys/utsname.h>

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

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

Risultato:

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

Fare riferimento a questa risposta .

Implementazione del codice completo:

#import <sys/utsname.h>

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

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

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

    return strModelID;
}

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

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

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

    return [notchiModels containsObject:GetDeviceModel()];
}

1
Risposta eccellente poiché gestisce correttamente il simulatore. Aggiungi la riga #import alla sezione "codice completo". L'ho perso (copia / incollato) al mio primo tentativo.
mpoisot,

1
questo è il mio metodo preferito. Fare riferimento a questo wiki per un elenco completo delle stringhe del modello di dispositivo. Come commento laterale, @ "iphone10,3" potrebbe anche essere visto come codice hard.
YvesLeBorg,

1
@YvesLeBorg Sì, è davvero un problema controverso critico. La stringa del modello hardware ha un identificatore univoco rispetto ai punti dello schermo per il dispositivo, credo. Generalmente, viene utilizzato per le statistiche dei dati.
Itachi,

25
#define IS_IPHONE        (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS  (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_IPHONE_X      (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)

definire IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] limiti] .size.height == 812.0)

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

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

Nota: - Fare attenzione, funziona benissimo solo per l'orientamento verticale


2
Fai

1
Grazie per questo. Funziona bene. In modalità orizzontale è necessario regolare tali numeri. Il numero magico di iPhoneX in modalità orizzontale è 375.0
pvella

Ci sono alcuni iPhone Plus / Max / Pro in uso nativeScalecon 3.0, giusto?
Itachi

24

Dopo aver visto tutte le risposte, questo è quello che ho finito per fare:

Soluzione (compatibile con Swift 4.1)

extension UIDevice {
    static var isIphoneX: Bool {
        var modelIdentifier = ""
        if isSimulator {
            modelIdentifier = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
        } else {
            var size = 0
            sysctlbyname("hw.machine", nil, &size, nil, 0)
            var machine = [CChar](repeating: 0, count: size)
            sysctlbyname("hw.machine", &machine, &size, nil, 0)
            modelIdentifier = String(cString: machine)
        }

        return modelIdentifier == "iPhone10,3" || modelIdentifier == "iPhone10,6"
    }

    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }
}

Uso

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

Nota

Pre Swift 4.1 è possibile verificare se l'app è in esecuzione su un simulatore in questo modo:

TARGET_OS_SIMULATOR != 0

Da Swift 4.1 e versioni successive puoi verificare se l'app è in esecuzione su un simulatore utilizzando le condizioni della piattaforma dell'ambiente di destinazione :

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

(il metodo precedente funzionerà ancora, ma questo nuovo metodo è più a prova di futuro)


Apple sta bene con questo?
Surjeet Rajput,

@ commando24 Sì, non vedo alcun motivo per rifiutare l'app a causa di questo codice.
Cloud9999

18

Tutte queste risposte basate sulle dimensioni sono suscettibili a comportamenti errati su dispositivi futuri. Funzioneranno oggi, ma se l'anno prossimo ci fosse un iPhone che ha le stesse dimensioni ma ha la fotocamera, ecc. Sotto il vetro, quindi non c'è "tacca?" Se l'unica opzione è aggiornare l'app, allora è una soluzione scadente per te e i tuoi clienti.

Puoi anche controllare la stringa del modello hardware come "iPhone10,1", ma questo è problematico perché a volte Apple rilascia numeri di modello diversi per operatori diversi in tutto il mondo.

L'approccio corretto è ridisegnare il layout superiore o risolvere i problemi che si verificano con l'altezza della barra di navigazione personalizzata (questo è ciò su cui mi concentrerei). Ma, se decidi di non fare nessuna di queste cose, renditi conto che qualunque cosa tu stia facendo è un hack per farlo funzionare oggi , e dovrai correggerlo ad un certo punto, forse più volte, per mantenere gli hack Lavorando.


1
Destra. Affinando un'ipotesi che il numero X sarà sempre A in uno, il numero X sarà sempre A a meno che la condizione Y quando sarà B sta solo scavando più a fondo. Dimensioni basate sull'area sicura nominata da Apple, non indovinandola.
Tommy,

2
Mi preoccuperò per il prossimo iPhone quando sarà effettivamente là fuori. Voglio che la mia app funzioni OGGI.
Vahid Amiri,

13

Risposta SWIFT 4+

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

Nota: è necessario un dispositivo reale per il test

Riferimento

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

Per il metodo 1, è possibile rimuovere la proprietà "var window" all'esterno della funzione e solo una costante "let" al suo interno (digitare UIWindow, ovvero non facoltativo). Mi piace questa risposta poiché all'avvio, self.view.window potrebbe essere zero e UIApplication.shared.keyWindow potrebbe anche essere nulla, mentre la creazione di una UIWindow in questo modo funziona ogni volta.
Rolleric,

11

Estensione riutilizzabile SWIFT 4/5 con supporto per iPhone 11

    public extension UIDevice {

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

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

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

2
buona estensione, ma il più utile qui èUIDevice.current.hasHomeButton
WINSergey,

1
@ale_stro è utile utilizzare userInterfaceIdiom per determinare i dispositivi per l'app universale? la maggior parte delle persone non lo consiglia. c'è qualche danno per usarlo?
Shaqir Saiyed,

10

Sì, è possibile. Scarica il estensione UIDevice-Hardware (o installa tramite CocoaPod 'UIDevice-Hardware') e quindi usa:

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

Si noti che questo non funzionerà nel simulatore, solo sul dispositivo reale.


Tutto il codice del dispositivo qui: iphonesoft.fr/2016/10/31/… Esempio: iPhone X: iPhone10,5 e iPhone10,6
Medhi

Le stringhe Hardware di Wikipedia hanno detto "iPhone10,3 e iPhone10,6". @Medhi
Itachi,

@Medhi, è possibile utilizzare ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]in Simulator per ottenere i valori effettivi da Xcode.
Cœur

9

Secondo la risposta di @ saswanb, questa è una versione di Swift 4:

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

La barra di stato è anche considerata al di fuori dell'area sicura! quindi questo restituirà falsi positivi! Dovrebbe essere superiore a 20 punti (altezza della barra di stato). Questo vale anche se il dispositivo è iPhone Xs, R o Xs Max.
MQoder

il codice funziona alla grande, ma fai attenzione: keyWindowè nilfino a quando il controller della vista principale non ha chiamatoviewDidAppear
Casey

9

So che è solo una soluzione Swift , ma potrebbe aiutare qualcuno.

Ho globals.swiftin ogni progetto e una delle cose che aggiungo sempre è DeviceTyperilevare facilmente il dispositivo dell'utente:

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

Quindi per usarlo:

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

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

Se usi LaunchImagenel tuo progetto, assicurati di aggiungere immagini per tutti i dispositivi supportati (come XS Max, XR) perché UIScreen.main.boundsnon restituirà il valore corretto senza quelli.


1
L'amico nuovo di Swift ha chiesto come usarlo, nel caso in cui qualcun altro non lo sappia ... if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling

5

Tutte le risposte che stanno usando heightsono solo metà parte della storia per una ragione. Se hai intenzione di verificare in questo modo quando l'orientamento del dispositivo è landscapeLefto landscapeRightil controllo fallirà, perché heightviene sostituito con width.

Ecco perché la mia soluzione è simile a questa in 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
    }
}

Usa invece nativeBounds invece
Leo Dabus,

4

Non dovresti supporre che l'unico dispositivo rilasciato da Apple con una diversa altezza UINavigationBar sarà l'iPhone X. Prova a risolvere questo problema utilizzando una soluzione più generica. Se vuoi che la barra sia sempre 20px più grande della sua altezza predefinita, il tuo codice dovrebbe aggiungere 20px all'altezza della barra, invece di impostarla su 64px (44px + 20px).


Quindi, quale altra soluzione devi proporre?
Stephane Mathis,

@xaphod ora ci sono risposte migliori.
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:

senza bisogno di alcun valore di pixel delle dimensioni del dispositivo

//UIApplication+SafeArea.swift

extension UIApplication { 

    static var isDeviceWithSafeArea:Bool {

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

        return false
    }
}

Esempio:

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
ti restituirà 812 se carichi l'immagine predefinita per iPhone X. Fino ad allora penso che ti restituirà le dimensioni di iPhone 7, non sono sicuro però ...
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
Migliore risposta! Senza bisogno di alcun valore in pixel delle dimensioni del dispositivo.
Peter Kreinz,

3

Di solito, il programmatore ne ha bisogno per limitare in alto o in basso, quindi questi metodi possono aiutare

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
}

Prima di iPhone X questi metodi restituiscono: 0

Per iPhone X: 44 e 34 di conseguenza

Quindi aggiungi questi extra ai vincoli top o bottom


3

Per quelli che ottengono 2001px invece di 2436px per l'altezza dei limiti nativi (come me), è perché hai creato la tua app con un SDK più vecchio, prima di iOS 11 (Xcode 8 invece di Xcode 9). Con un SDK più vecchio, iOS visualizzerà le app "in scatola nera" sull'iPhone X invece di estendere lo schermo da bordo a bordo, oltre la "tacca del sensore" superiore. Ciò riduce le dimensioni dello schermo, motivo per cui quella proprietà restituisce 2001 invece di 2436.

La soluzione più semplice è quella di controllare entrambe le dimensioni se sei interessato solo al rilevamento del dispositivo. Ho usato questo metodo per rilevare FaceID mentre costruivo con un Xcode SDK più vecchio che non ha il valore ENUM che specifica il tipo biometrico. In questa situazione, il rilevamento del dispositivo utilizzando l'altezza dello schermo sembrava il modo migliore per sapere se il dispositivo aveva FaceID vs TouchID senza dover aggiornare Xcode.


3

NON utilizzare le dimensioni dei pixel dello schermo come suggerito da altre soluzioni, ciò è negativo in quanto può provocare falsi positivi per i dispositivi futuri; non funzionerà se UIWindow non ha ancora eseguito il rendering (AppDelegate), non funzionerà nelle app orizzontali e può fallire nel simulatore se è impostata la scala.

Ho, invece, creato una macro per questo scopo, è molto facile da usare e si affida a flag hardware per prevenire i problemi di cui sopra.

Modifica: aggiornato per supportare iPhoneX, iPhone XS, iPhoneXR, iPhoneXS max


Usare:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Sì, davvero.


macro:

Basta copiare incollarlo ovunque, preferisco il fondo del mio file .h dopo @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]);\
})()\
)

L'unico motivo per cui posso pensare di rilevare iPhoneX è evitare la tacca nella parte superiore dello schermo; in tal caso è possibile controllare safeArea.top per rilevare la dimensione di detta tacca. Assicurati solo di misurarlo dopo che UIWindow è stato caricato, quindi non durante viewDidLoad ma un ciclo di thread dopo:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw,

2

Ho elaborato le tue risposte altrui e ho fatto una rapida estensione su UIDevice. Mi piacciono gli enumeratori rapidi e "tutto in ordine" e atomizzati. Ho creato una soluzione che funziona sia su dispositivo che su simulatore.

Vantaggi: - interfaccia semplice, utilizzo ad es. UIDevice.current.isIPhoneX -UIDeviceModelType enum ti dà la possibilità di estendere facilmente funzionalità e costanti specifiche del modello che desideri utilizzare nella tua app, ad esempio cornerRadius

Svantaggio: - è una soluzione specifica per modello, non specifica per la risoluzione - ad es. Se Apple produrrà un altro modello con le stesse specifiche, questo non funzionerà correttamente e devi aggiungere un altro modello per farlo funzionare => devi aggiornare il tuo 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
    }
}

Invece di usare Mirror, sarà più veloce usare sysctlbynamecome fatto nella risposta Cloud9999Strife (e anche nella mia risposta).
Cœur,

2

Mi affido all'altezza del riquadro della barra di stato per rilevare se si tratta di un iPhone X:

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

Questo è per l'applicazione un ritratto. È inoltre possibile verificare le dimensioni in base all'orientamento del dispositivo. Inoltre, su altri iPhone, la barra di stato potrebbe essere nascosta, quindi l'altezza della cornice è 0. Su iPhone X, la barra di stato non è mai nascosta.


Puoi nascondere la barra di stato di iPhoneX controllercon questo: - (BOOL)prefersStatusBarHidden { return YES; } Quindi l'altezza della barra di stato è 0.
无 夜 之 星辰

@ 无 夜 之 星辰 Lo controllo all'avvio in AppDelegate.
Tiois,

2

Stavo usando il codice di Peter Kreinz (perché era pulito e faceva quello che mi serviva) ma poi mi sono reso conto che funziona proprio quando il dispositivo è in verticale (dato che il padding superiore sarà in cima, ovviamente) Quindi ho creato un'estensione per gestire tutti i orientamenti con le rispettive imbottiture, senza inoltro sulla dimensione dello schermo:

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

E sul tuo sito di chiamata devi solo:

let res = UIDevice.current.isIphoneX

2

In alternativa, puoi controllare il pod " DeviceKit ". Una volta installato, tutto ciò che devi fare per verificare il dispositivo è:

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

2

Nov 2019:

Ecco cosa uso in tutti i miei progetti di produzione. Nota che questa sostanza è piuttosto lunga.

  1. Questo non utilizza calcoli di larghezza o altezza, ma piuttosto:
  2. Controlla il modello di stringa del dispositivo.
  3. Non ha il rischio che la tua build venga rifiutata da Apple a causa dell'utilizzo di API private / non documentate.
  4. Funziona con simulatori 💯

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

Utilizzo: lasciare inserto: CGFloat = DeviceUtility.isIphoneXType? 50.0: 40.0


Funziona perfettamente. Grazie. Lo sto usando in un progetto SwiftUI.
London

1

Di recente ho dovuto risolvere lo stesso problema. E mentre a questa domanda viene data una risposta definitiva ("No"), ciò può aiutare gli altri che hanno bisogno di comportamenti di layout specifici per iPhone X.

Non ero davvero interessato al fatto che il dispositivo fosse iPhone X. Mi interessava sapere se il dispositivo aveva un display dentellato.

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
}

Puoi anche scrivere a hasOnScreenHomeIndicator variabile lungo le stesse righe (anche se controlla l'area di sicurezza in basso, forse?).

Quanto sopra usa la mia estensione UIViewper un comodo accesso agli inserti delle aree sicure su iOS 10 e precedenti.

@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

Solo in Ritratto uso la larghezza e l'altezza della cornice della vista per controllare:

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

}

Le dimensioni dello schermo verticale sono elencate qui

inserisci qui la descrizione dell'immagine


0

Esistono diversi motivi per voler sapere qual è il dispositivo.

  1. È possibile controllare l'altezza (e la larghezza) del dispositivo. Questo è utile per il layout, ma di solito non vuoi farlo se vuoi conoscere il dispositivo esatto.

  2. Ai fini del layout, è anche possibile utilizzare UIView.safeAreaInsets.

  3. Se si desidera visualizzare il nome del dispositivo, ad esempio, da includere in un'e-mail a fini diagnostici, dopo aver recuperato il modello del dispositivo utilizzando sysctl (), è possibile utilizzare l'equivalente di questo per capire il nome:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.