Sprawdź, czy urządzeniem jest iPhone X.

262

Moja aplikacja na iOS używa niestandardowej wysokości, UINavigationBarco prowadzi do problemów w nowym iPhonie X.

Czy ktoś już wie, jak niezawodnie wykryć programowo (w Objective-C), czy aplikacja działa na iPhonie X?

EDYTOWAĆ:

Oczywiście możliwe jest sprawdzenie rozmiaru ekranu, jednak zastanawiam się, czy istnieje jakaś metoda „wbudowana”, taka jak TARGET_OS_IPHONEwykrywanie iOS ...

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

EDYCJA 2:

Nie sądzę, że moje pytanie jest duplikatem powiązanego pytania. Oczywiście istnieją metody „mierzenia” różnych właściwości bieżącego urządzenia i wykorzystywania wyników do decydowania, które urządzenie zostanie użyte. Jednak nie o to chodziło w moim pytaniu, które starałem się podkreślić w pierwszej edycji.

Rzeczywiste pytanie brzmi: „Czy można bezpośrednio wykryć, czy bieżącym urządzeniem jest iPhone X (np. Za pomocą funkcji SDK), czy też muszę korzystać z pomiarów pośrednich” ?

Na podstawie udzielonych odpowiedzi zakładam, że odpowiedź brzmi: „Nie, nie ma bezpośrednich metod. Pomiary są właściwą drogą”.

Andrei Herford
źródło
iPhone X ma inną rozdzielczość ekranu.
El Tomato
2
Tak, jak wspomniałem w mojej edycji, można sprawdzić rozmiar ekranu. Pytanie jednak brzmi, czy istnieje „bezpośrednia” metoda zapytania o typ urządzenia zamiast „pośrednich” pomiarów ...
Andrei Herford,
3
Autor chce tylko uzyskać typ urządzenia, a nie rozdzielczość ekranu. Dlaczego nie sprawdzić bezpośrednio nazwy maszyny? @lubilis ma rację.
Itachi,
2
dlaczego nie skorzystasz z przewodników po bezpiecznych obszarach, tak jak zaleca Apple?
holex
4
WAŻNE, przyszli twórcy: Nie wykrywaj tej wysokości ekranu, jak sugerują obecne najlepsze rozwiązania, jest to złe, ponieważ może powodować fałszywe alarmy dla przyszłych urządzeń; nie będzie działać, jeśli UIWindow nie został jeszcze zrenderowany (jak w funkcjach inicjujących AppDelegate), nie będzie działać w aplikacjach poziomych i może nie działać na symulatorze, jeśli ustawiona jest skala. NIGDY nie używaj magicznych liczb do takich rzeczy! Możesz sprawdzić flagi sprzętu, aby zagwarantować sukces, tak jak zrobiłem tutaj: stackoverflow.com/a/51511947/2057171
Albert Renshaw

Odpowiedzi:

383

Na podstawie twojego pytania odpowiedź brzmi „nie”. Nie ma bezpośrednich metod. Aby uzyskać więcej informacji, możesz uzyskać informacje tutaj:

i

Wysokość iPhone'a X wynosi 2436 pikseli

Z ekranu urządzenia Rozmiary i rozdzielczości :

wprowadź opis zdjęcia tutaj

Od urządzeniu ekranu Rozmiary i orientacje :

wprowadź opis zdjęcia tutaj

Swift 3 i nowszy :

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

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

W oparciu o następujące pytanie:

Lub użyj screenSize.heightjako float 812.0f not 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");
    }

Aby uzyskać więcej informacji, zapoznaj się z następującą stroną w Wytycznych dla interfejsu człowieka iOS:

Szybki :

Wykryj za pomocą topNotch:

Jeśli ktoś rozważa użycie wycięcia do wykrycia iPhoneX, pamiętaj, że w „ustawieniu poziomym” jest tak samo dla wszystkich iPhone'ów.

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
}

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

AKTUALIZACJA :

Nie używaj tej userInterfaceIdiomwłaściwości do identyfikowania typu urządzenia, ponieważ dokumentacja dla userInterfaceIdiom wyjaśnia:

W przypadku uniwersalnych aplikacji możesz użyć tej właściwości, aby dostosować zachowanie aplikacji do określonego typu urządzenia. Na przykład urządzenia iPhone i iPad mają różne rozmiary ekranu, więc możesz chcieć utworzyć różne widoki i elementy sterujące w zależności od typu bieżącego urządzenia.

Oznacza to, że ta właściwość służy tylko do identyfikacji stylu widoku uruchomionej aplikacji. Jednak aplikacja iPhone (nie uniwersalna) może zostać zainstalowana na urządzeniu iPad za pośrednictwem App Store, w takim przypadku również ją userInterfaceIdiomzwróci UIUserInterfaceIdiomPhone.

Właściwy sposób to uzyskanie nazwy komputera uname. Sprawdź następujące szczegóły:

Anbu.Karthik
źródło
Rozdzielczość iPhone'a X wynosi 2436 x 1125 pikseli zgodnie z: iphonesoft.fr/2017/09/12/…
Medhi
1
@Medhi - rozdzielczość iPhone'a X wynosi - 1125 x 2436 pikseli (gęstość pikseli ~ 458 ppi)
Anbu.Karthik
14
NIE! Aplikacja na iPhone'a (nie wszechświat) może zostać zainstalowana na urządzeniu iPad za pośrednictwem App Store, w takim przypadku również jąuserInterfaceIdiomzwróciUIUserInterfaceIdiomPhone. Ta odpowiedź jest zła.
Itachi,
1
@ ThreeCoins, zaktualizuj swoją odpowiedź dla urządzeń plus zgodnie z sugestią Leo Dabusa. Działa na symulatorze Plus, ale nie na urządzeniu.
Hiren Gujarati
2
Jest to złe, ponieważ może powodować fałszywe alarmy dla przyszłych urządzeń; nie będzie działać, jeśli UIWindow nie został jeszcze zrenderowany (AppDelegate), nie będzie działać w aplikacjach poziomych i może zawieść na symulatorze, jeśli ustawiona jest skala. Możesz sprawdzić flagi sprzętu, aby zagwarantować sukces, tak jak ja to zrobiłem tutaj: stackoverflow.com/a/51511947/2057171
Albert Renshaw
101

Inna możliwość, która działa na iOS 11 i iOS 12, ponieważ iPhone X jest jedynym z wycięciem u góry i wstawką 44. Właśnie to tutaj wykrywam:

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

I oczywiście może zajść potrzeba sprawdzenia wstawek lewego i prawego obszaru bezpiecznego, jeśli jesteś w orientacji poziomej.

Edycja: _window to UIWindow AppDelegate, gdzie to sprawdzenie jest wykonywane w aplikacji didFinishLaunchingWithOptions.

Odpowiedź zaktualizowana dla iOS 12, aby sprawdzić, czy top> 24 zamiast top> 0.

Edycja: W symulatorze możesz przejść do Sprzęt, Przełącz pasek stanu podczas połączenia. To pokazuje, że wysokość paska stanu nie zmienia się w telefonie iPhone X w systemie iOS 11 lub iPhone XS iOS 12 po nawiązaniu połączenia. Wszystko, co się zmienia, to ikona czasu, która w obu przypadkach ma zielone tło. Oto przystawka:

wprowadź opis zdjęcia tutaj

saswanb
źródło
5
Wstawki obszaru bezpiecznego będą zawierać wysokość paska stanu, jeśli jest ona widoczna, na innych urządzeniach. Sprawdzenie, czy jest to 0, pokaże tylko, czy pasek stanu jest widoczny, a nie jeśli urządzenie to iPhone X.
IMcD23,
3
„Może to uszkodzić iPhone'a Xs lub iPhone'a 11 ”, powiedział Cook.
Itachi
11
Zaadaptowałem trochę i używam, if _window.safeAreaInsets != UIEdgeInsets.zeroaby umożliwić dowolną orientację urządzenia
Fraser
2
Jeśli nie chcesz używać .top, safeAreaInsets.bottombędzie 34 na iPhone X i 0 na innych urządzeniach
blwinters
7
Ostrzeżenie: nie używaj tego, psuje się w iOS 12. Nie jest również udokumentowane, co UIWindow powinien zrobić w tym przypadku. openradar.appspot.com/42372793
steipete
73

Będziesz wykonywać różne wykrycia iPhone'a X w zależności od faktycznej potrzeby.

do radzenia sobie z najwyższym poziomem (pasek stanu, pasek nawigacyjny) itp.

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
}

do radzenia sobie z dolnym wskaźnikiem głównym (pasek narzędzi) itp.

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
}

rozmiar tła, funkcje pełnoekranowe itp.

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

Uwaga: ostatecznie wymieszaj z UIDevice.current.userInterfaceIdiom == .phone
Uwaga: ta metoda wymaga posiadania scenorysu LaunchScreen lub odpowiedniego LaunchImages

dla współczynnika tła, funkcji przewijania itp.

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
}

Uwaga: ta metoda wymaga posiadania scenorysu LaunchScreen lub odpowiedniego LaunchImages

do analiz, statystyk, śledzenia itp.

Uzyskaj identyfikator maszyny i porównaj go z udokumentowanymi wartościami:

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

Aby dołączyć symulator jako prawidłowego iPhone'a X do analizy:

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

Aby dołączyć iPhone'a XS, XS Max i XR, po prostu wyszukaj modele zaczynające się od „iPhone11”:

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

do obsługi 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
}
Cœur
źródło
Miałem nadzieję, return LAContext().biometryType == .typeFaceIDże zadziała, nawet jeśli użytkownik odmówił canEvaluatePolicy, ale to nie działa dla mnie, wciąż powraca.none
Jeremy,
Cóż, Jeremy, jest to udokumentowane zachowanie, będące konsekwencją polityki prywatności Apple. Dlatego komentarz powyżej metody.
Cœur
Ach, źle zinterpretowałem twój komentarz. Myślałem, że masz na myśli użycie canEvaluatePolicy może się nie powieść, więc zamiast tego użyj następujących. Uważam, że to trochę dziwne, że wolno ci sprawdzać, czy urządzenie ma Face ID, dopóki użytkownik nie zareaguje na przełączenie, a wtedy nie możesz już nawet sprawdzić. Jak mam przekazać pomocny komunikat o błędzie, aby przejść do Ustawień i przełączać identyfikator twarzy?
Jeremy,
@Jeremy Nie posiadam iPhone'a X, więc nie wiem. Być może możesz użyć funkcji wykrywania modelu powyżej ( model == "iPhone10,3" || model == "iPhone10,6"), a jeśli canUseFaceIDzwróci false, oznacza to, że użytkownik odmówił.
Cœur
1
@MateoOlaya Nic w mojej odpowiedzi nie zostałoby odrzucone przez Apple: możesz użyć wszystkiego.
Cœur
42

Możesz to zrobić, aby wykryć urządzenie iPhone X według wymiaru.

Szybki

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

Cel C

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

wprowadź opis zdjęcia tutaj

ale ,

To nie jest wystarczający sposób. Co jeśli Apple ogłosi kolejny iPhone o takim samym wymiarze jak iPhone X. więc najlepszym sposobem jest użycie ciągu sprzętowego do wykrycia urządzenia.

W przypadku nowszych urządzeń Ciąg sprzętu jest jak poniżej.

iPhone 8 - iPhone10,1 lub iPhone 10,4

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

iPhone X - iPhone10,3 lub iPhone10,6

Jaydeep
źródło
2
Powinieneś użyć [UIDevice currentDevice]zamiast[[UIDevice alloc] init]
S. Matsepura
jedynym problemem z ciągiem sprzętowym jest to, że nie działa na symulatorze
cichy
38

Sprawdź model urządzenia / nazwę maszyny , NIE używaj liczby punktów / pikseli bezpośrednio w kodzie, jest to twardy kod i nie ma znaczenia dla sprzętu urządzenia, model urządzenia jest jedynym unikalnym identyfikatorem dla odpowiedniego typu urządzenia .

#import <sys/utsname.h>

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

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

Wynik:

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

Zobacz tę odpowiedź .

Pełna implementacja kodu:

#import <sys/utsname.h>

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

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

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

    return strModelID;
}

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

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

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

    return [notchiModels containsObject:GetDeviceModel()];
}
Itachi
źródło
1
Doskonała odpowiedź, ponieważ poprawnie obsługuje symulator. Dodaj wiersz #import do sekcji „pełny kod”. Brakowało mi tego (kopiowanie / wklejanie) przy mojej pierwszej próbie.
mpoisot
1
to moja preferowana metoda. Zapoznaj się z tą wiki, aby uzyskać pełną listę ciągów modeli urządzeń. Jako komentarz boczny @ @ iphone10,3 może być również traktowany jako twardy kod.
YvesLeBorg
1
@YvesLeBorg Tak, to naprawdę bardzo kontrowersyjny problem. Myślę, że ciąg modelu sprzętowego ma unikalny identyfikator niż punkty ekranowe dla urządzenia. Zasadniczo służy do statystyki danych.
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)

zdefiniuj IS_IPHONE_X (IS_IPHONE i& [[granice ekranu głównego UIScreen]] .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"]

Uwaga: - Uważaj, działa dobrze tylko w przypadku orientacji pionowej

Jagveer Singh
źródło
2
Uważaj, działa dobrze tylko w przypadku orientacji pionowej
CFIFok
1
Dzięki za to. Działa dobrze. W trybie poziomym należy dostosować te liczby. Magiczna liczba iPhoneX w trybie poziomym to 375,0
pvella
Jest kilka iPhone'ów Plus / Max / Pro korzystających nativeScalez 3.0, prawda?
Itachi
24

Po przeanalizowaniu wszystkich odpowiedzi to właśnie skończyłem:

Rozwiązanie (kompatybilne z 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
    }
}

Posługiwać się

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

Uwaga

W wersji wcześniejszej niż Swift 4.1 możesz sprawdzić, czy aplikacja działa na symulatorze w następujący sposób:

TARGET_OS_SIMULATOR != 0

Począwszy od wersji Swift 4.1, możesz sprawdzić, czy aplikacja działa na symulatorze, używając warunku platformy środowiska docelowego :

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

(starsza metoda nadal będzie działać, ale ta nowa metoda jest bardziej przyszłościowa)

Cloud9999Strife
źródło
czy jabłko będzie z tym dobrze?
Surjeet Rajput,
@ commando24 Tak, nie widzę powodu, aby odrzucali aplikację z powodu tego kodu.
Cloud9999Strife
18

Wszystkie te odpowiedzi oparte na wymiarach są podatne na nieprawidłowe zachowanie na przyszłych urządzeniach. Będą działać dzisiaj, ale co będzie, jeśli w przyszłym roku będzie iPhone o tym samym rozmiarze, ale z aparatem itp. Pod szkłem, więc nie będzie „wycięcia”? Jeśli jedyną opcją jest aktualizacja aplikacji, jest to złe rozwiązanie dla Ciebie i Twoich klientów.

Możesz także sprawdzić ciąg modelu sprzętowego, taki jak „iPhone10,1”, ale jest to problematyczne, ponieważ czasami Apple wydaje różne numery modeli dla różnych operatorów na całym świecie.

Prawidłowe podejście polega na przeprojektowaniu górnego układu lub rozwiązaniu problemów z niestandardową wysokością paska nawigacyjnego (na tym właśnie się skupię). Ale jeśli zdecydujesz się nie robić żadnej z tych rzeczy, zdaj sobie sprawę, że to, co robisz, to hack, aby to zadziałało dzisiaj , i będziesz musiał to poprawić w pewnym momencie, być może wiele razy, aby utrzymać hacki pracujący.

clarus
źródło
1
Dobrze. Udoskonalenie założenia, że ​​liczba X będzie zawsze oznaczeniem A do jednego, że liczba X będzie zawsze oznaczeniem A, chyba że warunek Y, gdy będzie to B, po prostu kopie głębiej. Rozmiar oparty na bezpiecznym obszarze nominowanym przez Apple, a nie przez zgadywanie.
Tommy
2
Będę się martwił o następnego iPhone'a, kiedy już będzie dostępny. Chcę, aby moja aplikacja działała DZISIAJ.
Vahid Amiri,
13

Odpowiedź SWIFT 4+

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

Uwaga: Potrzebujesz prawdziwego urządzenia do testu

Odniesienie

 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
    }
}
Jacek
źródło
W przypadku metody 1 możesz usunąć właściwość „var window” poza func i po prostu stałą „let” w nim (wpisz UIWindow, tzn. Nie jest opcjonalny). Podoba mi się ta odpowiedź, ponieważ podczas uruchamiania okno self.view.window może być zerowe, a UIApplication.shared.keyWindow może być również zerowe, podczas gdy tworzenie UIWindow w ten sposób działa za każdym razem.
Rolleric
11

Wielokrotnego użytku rozszerzenie SWIFT 4/5 z obsługą iPhone'a 11

    public extension UIDevice {

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

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

    public var type: Type {
        if userInterfaceIdiom == .phone {
            switch UIScreen.main.nativeBounds.height {
            case 1136: return .iPhone_5_5S_5C
            case 1334: return .iPhone_6_6S_7_8
            case 1920, 2208: return .iPhone_6_6S_7_8_PLUS
            case 2436: return .iPhone_X_Xs
            case 2688: return .iPhone_Xs_11_Pro_Max
            case 1792: return .iPhone_Xr_11
            case 2426: return .iPhone_11_Pro
            default: return .iPhone_unknown
        }
        }
        return .iPad
   }
}
ale_stro
źródło
2
dobre rozszerzenie, ale najbardziej przydatne tutajUIDevice.current.hasHomeButton
WINSergey
1
@ale_stro, czy warto używać userInterfaceIdiom do określania urządzeń dla uniwersalnej aplikacji? większość ludzi tego nie zaleca. czy korzystanie z niego jest szkodliwe?
shaqir saiyed
10

Tak to mozliwe. Pobierz UIDevice-Hardware (lub zainstaluj przez CocoaPod „UIDevice-Hardware”), a następnie użyj:

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

Pamiętaj, że to nie będzie działać w Symulatorze, tylko na rzeczywistym urządzeniu.

Hendrik
źródło
Cały kod urządzenia tutaj: iphonesoft.fr/2016/10/31/… Przykład: iPhone X: iPhone10,5 i iPhone10,6
Medhi
Te ciągi sprzętowe z wikipedii powiedział „iPhone10,3 i iPhone10,6”. @Medhi
Itachi,
@Medhi, możesz użyć ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]w Symulatorze, aby uzyskać rzeczywiste wartości z Xcode.
Cœur
9

Zgodnie z odpowiedzią @ saswanb jest to wersja Swift 4:

var iphoneX = false
if #available(iOS 11.0, *) {
    if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
        iphoneX = true
    }
}
MattOZ
źródło
Pasek stanu jest również brany pod uwagę poza bezpiecznym obszarem! więc to zwróci fałszywe alarmy! Powinien być wyższy niż 20 punktów (wysokość paska stanu). Zwraca to również prawdę, jeśli urządzeniem jest iPhone Xs, R lub Xs Max.
MQoder
Kod działa świetnie, ale uważaj: keyWindowjest nilaż główny sterownik widok nazwałviewDidAppear
Casey
9

Wiem, że to tylko Szybki rozwiązanie, ale może komuś pomóc.

Mam globals.swiftw każdym projekcie i jedną z rzeczy, które zawsze dodam, jest DeviceTypełatwe wykrycie urządzenia użytkownika:

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

Następnie, aby go użyć:

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

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

Jeśli używasz LaunchImagew swoim projekcie, pamiętaj, aby dodać obrazy dla wszystkich obsługiwanych urządzeń (takich jak XS Max, XR), ponieważ UIScreen.main.boundsbez nich nie zwróci właściwej wartości.

budidino
źródło
1
Przyjaciel nowy Swift zapytał, jak korzystać z tego, na wszelki wypadek, gdyby ktoś jeszcze nie wie ... if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling
5

Wszystkie odpowiedzi, które wykorzystują, heightsą tylko połową historii z jednego powodu. Jeśli zamierzasz to sprawdzić, gdy orientacja urządzenia jest ustawiona, landscapeLeftlub landscapeRightsprawdzenie się nie powiedzie, ponieważ heightzmieniono z nawidth .

Właśnie dlatego moje rozwiązanie wygląda tak w Swift 4.0:

extension UIScreen {
    ///
    static var isPhoneX: Bool {
        let screenSize = UIScreen.main.bounds.size
        let width = screenSize.width
        let height = screenSize.height
        return min(width, height) == 375 && max(width, height) == 812
    }
}
DevAndArtist
źródło
Zamiast tego użyj po prostu nativeBounds
Leo Dabus
4

Nie należy zakładać, że jedynym urządzeniem, które Apple wypuści z inną wysokością paska UINavigationBar, będzie iPhone X. Spróbuj rozwiązać ten problem za pomocą bardziej ogólnego rozwiązania. Jeśli chcesz, aby pasek zawsze był o 20 pikseli większy niż domyślna wysokość, kod powinien dodać 20 pikseli do wysokości paska, zamiast ustawiać go na 64px (44px + 20px).

IMcD23
źródło
Jakie inne rozwiązanie musisz zaproponować?
Stephane Mathis,
@xaphod są teraz lepsze odpowiedzi.
Cœur
4
struct ScreenSize {
    static let width = UIScreen.main.bounds.size.width
    static let height = UIScreen.main.bounds.size.height
    static let maxLength = max(ScreenSize.width, ScreenSize.height)
    static let minLength = min(ScreenSize.width, ScreenSize.height)
    static let frame = CGRect(x: 0, y: 0, width: ScreenSize.width, height: ScreenSize.height)
}

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

    static let IS_IPAD              = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1024.0
    static let IS_IPAD_PRO          = UIDevice.current.userInterfaceIdiom == .pad && ScreenSize.maxLength == 1366.0
}
Kiran Sarvaiya
źródło
4

Swift 3 + 4:

bez potrzeby określania wielkości piksela urządzenia

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

Przykład:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}
Peter Kreinz
źródło
3
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
alexander.pan
źródło
2
zwróci ci 812, jeśli prześlesz domyślny obraz dla iPhone'a X. Do tego czasu myślę, że zwróci ci rozmiar iPhone'a 7, ale nie jestem pewien ...
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;
    }
}
użytkownik6788419
źródło
1
Najlepsza odpowiedź! Bez potrzeby określania wielkości piksela urządzenia.
Peter Kreinz,
3

Zwykle programista potrzebuje go do ograniczenia do góry lub do dołu, więc te metody mogą pomóc

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
}

Ponieważ przed iPhone X te metody zwracają: 0

Dla iPhone'a X: odpowiednio 44 i 34

Następnie po prostu dodaj te dodatki do górnych lub dolnych ograniczeń

Andrey
źródło
3

Dla tych, którzy otrzymali 2001px zamiast 2436px dla natywnej wysokości granic (jak ja), to dlatego, że zbudowałeś aplikację ze starszym SDK przed iOS 11 (Xcode 8 zamiast Xcode 9). W przypadku starszego zestawu SDK system iOS wyświetli aplikacje w „czarnej skrzynce” na iPhonie X zamiast rozszerzać ekran od krawędzi do krawędzi, wykraczając poza górny „wycięcie czujnika”. Zmniejsza to rozmiar ekranu i dlatego ta właściwość zwraca 2001 zamiast 2436.

Najprostszym rozwiązaniem jest sprawdzenie obu rozmiarów, jeśli interesuje Cię tylko wykrywanie urządzeń. Użyłem tej metody do wykrywania FaceID podczas budowania ze starszym zestawem SDK Xcode, który nie ma wartości ENUM określającej typ biometryczny. W tej sytuacji wykrywanie urządzenia za pomocą wysokości ekranu wydawało się najlepszym sposobem na sprawdzenie, czy urządzenie ma FaceID vs TouchID bez konieczności aktualizacji Xcode.

Jon Summers
źródło
3

NIE używaj rozmiaru piksela ekranu, jak sugerują inne rozwiązania, jest to złe, ponieważ może powodować fałszywe alarmy dla przyszłych urządzeń; nie będzie działać, jeśli UIWindow nie został jeszcze zrenderowany (AppDelegate), nie będzie działać w aplikacjach poziomych i może zawieść na symulatorze, jeśli ustawiona jest skala.

Zamiast tego stworzyłem makro do tego celu, jest bardzo łatwy w użyciu i polega na flagach sprzętowych, aby zapobiec wyżej wymienionym problemom.

Edycja: Zaktualizowano w celu obsługi iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Używać:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Tak, naprawdę.


Makro:

Po prostu skopiuj to wklej gdziekolwiek, wolę potem sam dół mojego pliku .h @end

#import <sys/utsname.h>

#if TARGET_IPHONE_SIMULATOR
#define IS_SIMULATOR YES
#else
#define IS_SIMULATOR NO
#endif

#define IS_DEVICE_IPHONEX (\
(^BOOL (void){\
NSString *__modelIdentifier;\
if (IS_SIMULATOR) {\
__modelIdentifier = NSProcessInfo.processInfo.environment[@"SIMULATOR_MODEL_IDENTIFIER"];\
} else {\
struct utsname __systemInfo;\
uname(&__systemInfo);\
__modelIdentifier = [NSString stringWithCString:__systemInfo.machine encoding:NSUTF8StringEncoding];\
}\
NSString *__iPhoneX_GSM_Identifier = @"iPhone10,6";\
NSString *__iPhoneX_CDMA_Identifier = @"iPhone10,3";\
NSString *__iPhoneXR_Identifier = @"iPhone11,8";\
NSString *__iPhoneXS_Identifier = @"iPhone11,2";\
NSString *__iPhoneXSMax_China_Identifier = @"iPhone11,6";\
NSString *__iPhoneXSMax_Other_Identifier = @"iPhone11,4";\
return ([__modelIdentifier isEqualToString:__iPhoneX_GSM_Identifier] || [__modelIdentifier isEqualToString:__iPhoneX_CDMA_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXR_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXS_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_China_Identifier] || [__modelIdentifier isEqualToString:__iPhoneXSMax_Other_Identifier]);\
})()\
)
Albert Renshaw
źródło
Jedynym powodem, dla którego mogę wykryć iPhoneX, jest uniknięcie wycięcia u góry ekranu; jeśli tak, możesz sprawdzić plik safeArea.top w celu wykrycia rozmiaru wspomnianego wycięcia. Tylko upewnij się, że zmierzysz go po załadowaniu UIWindow, więc nie podczas viewDidLoad, ale jeden cykl wątku po:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw
2

Opracowałem na twoich odpowiedziach na inne pytania i zrobiłem szybkie rozszerzenie na UIDevice. Lubię szybkie wyliczenia i „wszystko w porządku” i rozpylone. Stworzyłem rozwiązanie, które działa zarówno na urządzeniu, jak i na symulatorze.

Zalety: - prosty interfejs, użycie np. UIDevice.current.isIPhoneX -UIDeviceModelType enum daje możliwość łatwego rozszerzenia specyficznych dla modelu funkcji i stałych, których chcesz używać w swojej aplikacji, np. CornerRadius

Wada: - jest to rozwiązanie specyficzne dla modelu, a nie specyficzne dla rozdzielczości - np. Jeśli Apple wyprodukuje inny model z tymi samymi specyfikacjami, to nie będzie działać poprawnie i musisz dodać inny model, aby to działało => musisz zaktualizować swój 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
    }
}
koń śmierci
źródło
Zamiast używać Mirror, będzie szybciej używać, sysctlbynamejak zrobiono w odpowiedzi Cloud9999Strife (i również w mojej odpowiedzi).
Cœur
2

Opieram się na wysokości ramki paska stanu, aby wykryć, czy jest to iPhone X:

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

To jest dla aplikacji un portret. Możesz również sprawdzić rozmiar zgodnie z orientacją urządzenia. Na innych iPhone'ach pasek stanu może być ukryty, więc wysokość ramki wynosi 0. W telefonie iPhone X pasek stanu nigdy nie jest ukryty.

Tiois
źródło
Dzięki temu możesz ukryć pasek statusu w pasku iPhoneX controller: - (BOOL)prefersStatusBarHidden { return YES; } Wówczas wysokość paska statusBar wynosi 0.
无 夜 之 星辰
@ 无 夜 之 星辰 Sprawdzam to podczas uruchamiania w AppDelegate.
Tiois
2

I był przy użyciu kodu Petera Kreinz użytkownika (ponieważ był czysty i zrobił to, co potrzebne), ale potem zdałem sobie sprawę, że działa tylko wtedy, gdy urządzenie jest włączone portret (od najwyższej wyściółka będzie na wierzchu, oczywiście), więc stworzył rozszerzenie do obsługi wszystkich orientacje z odpowiednimi wypełnieniami, bez przekazywania rozmiaru ekranu:

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

A w witrynie połączeń po prostu:

let res = UIDevice.current.isIphoneX
rgkobashi
źródło
2

Alternatywnie możesz sprawdzić kapsułkęDeviceKit ”. Po zainstalowaniu wszystko, co musisz zrobić, aby sprawdzić urządzenie to:

import DeviceKit
let device = Device()
if device == .iPhoneX {
  // place your code here
}
Islombek Hasanov
źródło
2

Lis 2019:

Oto, czego używam we wszystkich moich projektach produkcyjnych. Zauważ, że ta istota jest dość długa.

  1. Nie używa to obliczeń szerokości lub wysokości, ale:
  2. Sprawdza model ciągu urządzenia.
  3. Nie ma ryzyka odrzucenia twojej kompilacji przez Apple z powodu użycia jakichkolwiek prywatnych / nieudokumentowanych interfejsów API.
  4. Działa z symulatorami 💯

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

Zastosowanie: niech wstawka: CGFloat = DeviceUtility.isIphoneXType? 50,0: 40,0

Glenn
źródło
Działa świetnie. Dzięki. Używam go w projekcie SwiftUI.
LondonGuy,
1

Ostatnio musiałem rozwiązać ten sam problem. I choć odpowiedź na to pytanie jest ostateczna („Nie”), może to pomóc innym, którzy potrzebują określonego układu iPhone'a X.

Naprawdę nie interesowało mnie, czy urządzeniem jest iPhone X. Byłem zainteresowany, czy urządzenie ma ząbkowany wyświetlacz.

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
}

Możesz także napisać hasOnScreenHomeIndicatorzmienną wzdłuż tych samych linii (choć sprawdź dolny bezpieczny obszar, może?).

Powyższe używa mojego rozszerzenia UIViewdla wygodnego dostępu do wstawek do bezpiecznego obszaru na iOS 10 i wcześniejszych.

@objc public extension UIView {
    @objc public var compatibleSafeAreaInsets: UIEdgeInsets {
        if #available(iOS 11.0, *) {
            return safeAreaInsets
        } else {
            return .zero
        }
    }

    @objc public var compatibleSafeAreaLayoutGuide: UILayoutGuide {
        if #available(iOS 11.0, *) {
            return safeAreaLayoutGuide
        } else {
            return layoutMarginsGuide
        }
    }
}
simeon
źródło
1

W Portret tylko używam szerokość widoku Szkielet i wysokość, aby sprawdzić:

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

}

Wymiary ekranu pionowego są wymienione tutaj

wprowadź opis zdjęcia tutaj

Lance Samaria
źródło
0

Istnieje kilka powodów, dla których warto wiedzieć, jakie jest urządzenie.

  1. Możesz sprawdzić wysokość (i szerokość) urządzenia. Jest to przydatne w przypadku układu, ale zwykle nie chcesz tego robić, jeśli chcesz znać dokładne urządzenie.

  2. Do celów układu możesz także użyć UIView.safeAreaInsets.

  3. Jeśli chcesz na przykład wyświetlić nazwę urządzenia, która ma być zawarta w wiadomości e-mail w celach diagnostycznych, po pobraniu modelu urządzenia za pomocą sysctl (), możesz użyć odpowiednika tego do zobrazowania nazwy:

    $ curl http://appledevicenames.com/devices/iPhone10,6
    
    iPhone X
Hwee-Boon Yar
źródło