Detectar se o dispositivo é iPhone X

262

Meu aplicativo iOS usa uma altura personalizada, o UINavigationBarque leva a alguns problemas no novo iPhone X.

Alguém já sabe como detectar de forma confiável por meio de programação (no Objective-C) se um aplicativo está sendo executado no iPhone X?

EDITAR:

Claro que é possível verificar o tamanho da tela, no entanto, gostaria de saber se existe algum método de "compilação" como TARGET_OS_IPHONEpara detectar o iOS ...

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

EDIT 2:

Eu não acho que minha pergunta seja uma duplicata da questão vinculada. Obviamente, existem métodos para "medir" diferentes propriedades do dispositivo atual e usar os resultados para decidir qual dispositivo será usado. No entanto, esse não era o ponto real da minha pergunta, como tentei enfatizar na minha primeira edição.

A questão real é: "É possível detectar diretamente se o dispositivo atual é um iPhone X (por exemplo, por algum recurso do SDK) ou preciso usar medições indiretas" ?

Pelas respostas dadas até agora, suponho que a resposta seja "Não, não há métodos diretos. As medidas são o caminho a percorrer".

Andrei Herford
fonte
O iPhone X possui uma resolução de tela diferente das outras.
El Tomato
2
Sim, como mencionei na minha edição, é possível verificar o tamanho da tela. No entanto, a questão é, se há um método "direto" para consultar o tipo de dispositivo em vez de medidas "indiretas" ...
Andrei Herford
3
O autor só deseja obter o tipo de dispositivo, não a resolução da tela. Por que não verificar o nome da máquina diretamente? @lubilis está certo.
Itachi
2
Por que você não usa apenas os guias da área de segurança, conforme recomendado pela Apple?
21917 holex
4
IMPORTANTES, futuros desenvolvedores: não detecte essa altura da tela, como sugerem as principais soluções atuais; é ruim, pois pode resultar em falsos positivos para dispositivos futuros; não funcionará se o UIWindow ainda não tiver sido renderizado (como nas funções de inicialização do AppDelegate), não funcionará em aplicativos de paisagem e poderá falhar no simulador se a escala estiver definida. NUNCA use números mágicos para coisas assim! Você pode verificar bandeiras de hardware para garantia de sucesso como eu fiz aqui: stackoverflow.com/a/51511947/2057171
Albert Renshaw

Respostas:

383

Com base na sua pergunta, a resposta é não. Não há métodos diretos. Para mais informações, você pode obter as informações aqui:

e

A altura do iPhone X é 2436 px

Em tamanhos e resoluções de tela do dispositivo :

insira a descrição da imagem aqui

Em tamanhos e orientações da tela do dispositivo :

insira a descrição da imagem aqui

Swift 3 e posterior :

if UIDevice().userInterfaceIdiom == .phone {
    switch UIScreen.main.nativeBounds.height {
        case 1136:
            print("iPhone 5 or 5S or 5C")

        case 1334:
            print("iPhone 6/6S/7/8")

        case 1920, 2208:
            print("iPhone 6+/6S+/7+/8+")

        case 2436:
            print("iPhone X/XS/11 Pro")

        case 2688:
            print("iPhone XS Max/11 Pro Max")

        case 1792:
            print("iPhone XR/ 11 ")

        default:
            print("Unknown")
        }
    }

Objetivo-C :

if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    switch ((int)[[UIScreen mainScreen] nativeBounds].size.height) {
        case 1136:
            printf("iPhone 5 or 5S or 5C");
                break;

        case 1334:
            printf("iPhone 6/6S/7/8");
            break;

        case 1920, 2208:
            printf("iPhone 6+/6S+/7+/8+");
            break;

       case 2436:
            print("iPhone X/XS/11 Pro");
             break;

        case 2688:
            print("iPhone XS Max/11 Pro Max");
             break;

        case 1792:
            print("iPhone XR/ 11 ");
             break;

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

Xamarin.iOS :

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

Com base na sua pergunta, como segue:

Ou use screenSize.heightcomo float 812.0f não int 812.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
        // 812.0 on iPhone X, XS
        // 896.0 on iPhone XS Max, XR.

    if (screenSize.height >= 812.0f)
        NSLog(@"iPhone X");
    }

Para obter mais informações, consulte a seguinte página nas Diretrizes da interface humana do iOS:

Swift :

Detectar com topNotch:

Se alguém pensar em usar o entalhe para detectar o iPhoneX, lembre-se de que na "paisagem" é o mesmo para todos os iPhones.

var hasTopNotch: Bool {
    if #available(iOS 13.0,  *) {
        return UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.safeAreaInsets.top ?? 0 > 20
    }else{
     return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
    }

    return false
}

Objetivo-C :

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

- (UIWindow*)keyWindow {
    UIWindow        *foundWindow = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            foundWindow = window;
            break;
        }
    }
    return foundWindow;
}

ATUALIZAÇÃO :

Não use a userInterfaceIdiompropriedade para identificar o tipo de dispositivo, como explica a documentação para userInterfaceIdiom :

Para aplicativos universais, você pode usar esta propriedade para personalizar o comportamento do seu aplicativo para um tipo específico de dispositivo. Por exemplo, os dispositivos iPhone e iPad têm tamanhos de tela diferentes, portanto, convém criar visualizações e controles diferentes com base no tipo do dispositivo atual.

Ou seja, essa propriedade é usada apenas para identificar o estilo de exibição do aplicativo em execução. No entanto, o aplicativo para iPhone (não o universal) pode ser instalado no dispositivo iPad via App Store; nesse caso, o userInterfaceIdiomretorno UIUserInterfaceIdiomPhonetambém será.

O caminho certo é obter o nome da máquina via uname. Verifique o seguinte para obter detalhes:

Anbu.Karthik
fonte
A resolução do iPhone X é de 2436 x 1125 pixels, de acordo com: iphonesoft.fr/2017/09/12/…
Medhi
1
@Medhi - a resolução do iphone X é - 1125 x 2436 pixels (~ 458 ppi densidade de pixels)
Anbu.Karthik
14
NÃO! O aplicativo para iPhone (não o universo) pode ser instalado no dispositivo iPad via App Store; nesse caso, ouserInterfaceIdiomretornoUIUserInterfaceIdiomPhonetambém será. Esta resposta está errada.
Itachi
1
@ThreeCoins, atualize sua resposta para dispositivos positivos, conforme sugestão de Leo Dabus. Funciona no simulador Plus, mas não no dispositivo.
precisa
2
Isso é ruim, pois pode resultar em falsos positivos para dispositivos futuros; não funcionará se o UIWindow ainda não tiver sido renderizado (AppDelegate), não funcionará em aplicativos de paisagem e poderá falhar no simulador se a escala estiver definida. Você pode verificar bandeiras de hardware para garantia de sucesso como eu fiz aqui: stackoverflow.com/a/51511947/2057171
Albert Renshaw
101

Outra possibilidade, que funciona no iOS 11 e no iOS 12, porque o iPhone X é o único com um entalhe na parte superior e uma inserção de 44. É isso que estou realmente detectando aqui:

Objetivo-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, é claro, pode ser necessário verificar as inserções da área segura esquerda e direita se estiver na orientação paisagem.

Edit: _window é a UIWindow do AppDelegate, onde essa verificação é feita no aplicativo didFinishLaunchingWithOptions.

Resposta atualizada para iOS 12 para verificar se top> 24 em vez de top> 0.

Editar: no simulador, você pode acessar Hardware, Alternar barra de status de chamada. Fazer isso me mostra que a altura da barra de status não muda no iPhone X no iOS 11 ou no iPhone XS iOS 12 quando está envolvido em uma chamada. Tudo o que muda é o ícone do tempo, que obtém um fundo verde nos dois casos. Aqui está um piscar de olhos:

insira a descrição da imagem aqui

saswanb
fonte
5
As inserções da área segura conterão a altura da barra de status, se estiver visível, em outros dispositivos. Verificar se este é 0 só irá dizer-lhe se a barra de status é visível, não se o dispositivo é um X. iPhone
IMcD23
3
"Isso pode quebrar o iPhone Xs ou o iPhone 11 ", disse Cook.
Itachi
11
Eu adaptei um pouco e uso if _window.safeAreaInsets != UIEdgeInsets.zeropara permitir qualquer orientação do dispositivo
Fraser
2
Se você não quiser usar .top, safeAreaInsets.bottomserá 34 no iPhone X e 0 em outros dispositivos
blwinters
7
Aviso: não use isso, ele quebra no iOS 12. Também não está documentado o que o UIWindow deve fazer neste caso. openradar.appspot.com/42372793
steipete:
73

Você deve realizar diferentes detecções do iPhone X, dependendo da necessidade real.

para lidar com o nível superior (barra de status, barra de navegação) etc.

class var hasTopNotch: Bool {
    if #available(iOS 11.0, tvOS 11.0, *) {
        // with notch: 44.0 on iPhone X, XS, XS Max, XR.
        // without notch: 24.0 on iPad Pro 12.9" 3rd generation, 20.0 on iPhone 8 on iOS 12+.
        return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 24
    }
    return false
}

para lidar com o indicador inicial inferior (tabbar), etc.

class var hasBottomSafeAreaInsets: Bool {
    if #available(iOS 11.0, tvOS 11.0, *) {
        // with home indicator: 34.0 on iPhone X, XS, XS Max, XR.
        // with home indicator: 20.0 on iPad Pro 12.9" 3rd generation.
        return UIApplication.shared.delegate?.window??.safeAreaInsets.bottom ?? 0 > 0
    }
    return false
}

para tamanho de fundos, recursos de tela cheia etc.

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

Nota: eventualmente, misture-o com UIDevice.current.userInterfaceIdiom == .phone
Nota: esse método requer um storyboard LaunchScreen ou LaunchImages adequado

para proporção de fundos, recursos de rolagem etc.

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

Nota: esse método requer que haja um storyboard do LaunchScreen ou LaunchImages adequado

para análises, estatísticas, rastreamento etc.

Obtenha o identificador da máquina e compare-o com os valores documentados:

class var isIphoneX: Bool {
    var size = 0
    sysctlbyname("hw.machine", nil, &size, nil, 0)
    var machine = [CChar](repeating: 0, count: size)
    sysctlbyname("hw.machine", &machine, &size, nil, 0)
    let model = String(cString: machine)
    return model == "iPhone10,3" || model == "iPhone10,6"
}

Para incluir o simulador como um iPhone X válido em suas análises:

class var isIphoneX: Bool {
    let model: String
    if TARGET_OS_SIMULATOR != 0 {
        model = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] ?? ""
    } else {
        var size = 0
        sysctlbyname("hw.machine", nil, &size, nil, 0)
        var machine = [CChar](repeating: 0, count: size)
        sysctlbyname("hw.machine", &machine, &size, nil, 0)
        model = String(cString: machine)
    }
    return model == "iPhone10,3" || model == "iPhone10,6"
}

Para incluir o iPhone XS, XS Max e XR, basta procurar modelos começando com "iPhone11":

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

para suporte 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
fonte
Eu estava esperando que o return LAContext().biometryType == .typeFaceIDiria funcionar mesmo se o usuário negou canEvaluatePolicy, mas ele não funciona para mim, ele ainda retorna.none
Jeremy
Bem, Jeremy, é um comportamento documentado, consequência da política de privacidade da Apple. É por isso que o comentário acima do método.
Cœur
Ah, eu interpretei mal o seu comentário. Eu pensei que você quisesse usar canEvaluatePolicy poderia falhar, então use o seguinte. Acho um pouco estranho que você tenha permissão para verificar se o dispositivo possui o Face ID até que o usuário responda à alternância e você não possa mais verificar. Como devo fornecer uma mensagem de erro útil para dizer para ir para Configurações e alternar para o Face ID?
Jeremy
@ Jeremy Eu não tenho um iPhone X, então não sei. Talvez você possa usar a detecção de modelo acima ( model == "iPhone10,3" || model == "iPhone10,6") e, se canUseFaceIDretornar falso, significa que foi negado pelo usuário.
Cœur
1
@MateoOlaya Nada na minha resposta seria rejeitado pela Apple: você pode usar tudo.
28418 Cœur
42

Você pode fazer assim para detectar o dispositivo iPhone X de acordo com a dimensão.

Rápido

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

Objetivo - C

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

insira a descrição da imagem aqui

mas ,

Esta não é a maneira suficiente. E se a Apple anunciasse o próximo iPhone com a mesma dimensão do iPhone X. A melhor maneira é usar a cadeia de hardware para detectar o dispositivo.

Para dispositivos mais recentes, a sequência de hardware é a seguinte.

iPhone 8 - iPhone10,1 ou iPhone 10,4

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

iPhone X - iPhone10,3 ou iPhone10,6

Jaydeep
fonte
2
Você deve usar em [UIDevice currentDevice]vez de[[UIDevice alloc] init]
S. Matsepura 7/17/17
o único problema com a seqüência de hardware é que ele não funciona no simulador
quemeful
38

Verifique o modelo do dispositivo / nome da máquina , NÃO use a contagem de pontos / pixels no seu código diretamente, é código rígido e sem sentido para o hardware do dispositivo , o modelo do dispositivo é o único identificador exclusivo para um tipo de dispositivo correspondente .

#import <sys/utsname.h>

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

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

Resultado:

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

Consulte esta resposta .

Implementação de código completo:

#import <sys/utsname.h>

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

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

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

    return strModelID;
}

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

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

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

    return [notchiModels containsObject:GetDeviceModel()];
}
Itachi
fonte
1
Excelente resposta, pois manipula o simulador corretamente. Por favor, adicione a linha #import à seção "código completo". Eu perdi isso (copiar / colar) na minha primeira tentativa.
mpoisot
1
esse é o meu método preferido. Consulte este wiki para obter uma lista completa de sequências de modelos de dispositivos. Como comentário lateral, @ "iphone10,3" também pode ser visto como código rígido.
YvesLeBorg
1
@YvesLeBorg Sim, é realmente uma questão controversa crítica. A string do modelo de hardware tem um identificador único que os pontos de tela do dispositivo, eu acho. Geralmente, é usado para estatísticas de dados.
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)

defina IS_IPHONE_X (IS_IPHONE && [[tela principal do UIScreen] limites] .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: - Cuidado, ele funciona bem apenas para orientação retrato

Jagveer Singh
fonte
2
Tenha cuidado, ele funciona bem apenas para orientação retrato
CFIFok
1
Obrigado por isso. Funciona bem. No modo paisagem, você precisa ajustar esses números. O número mágico de iPhoneX no modo paisagem é 375,0
pvella
Existem alguns iPhone Plus / Max / Pro usando nativeScalecom 3.0, certo?
Itachi
24

Depois de analisar todas as respostas, foi isso que acabei fazendo:

Solução (compatível com 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
    }
}

Usar

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

Nota

No Pre Swift 4.1, você pode verificar se o aplicativo está sendo executado em um simulador como este:

TARGET_OS_SIMULATOR != 0

Do Swift 4.1 em diante, você pode verificar se o aplicativo está sendo executado em um simulador usando a condição da plataforma do ambiente Target :

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

(o método mais antigo ainda funcionará, mas esse novo método é mais à prova de futuro)

Cloud9999Strife
fonte
maçã vai ficar bem com isso?
Surjeet Rajput
@ commando24 Sim, não vejo motivo para eles rejeitarem o aplicativo por causa desse código.
Cloud9999Strife
18

Todas essas respostas baseadas em dimensões são suscetíveis a comportamentos incorretos em dispositivos futuros. Eles funcionarão hoje, mas e se houver um iPhone no próximo ano com o mesmo tamanho, mas com a câmera etc. embaixo do vidro, para que não haja entalhes? Se a única opção é atualizar o aplicativo, é uma solução ruim para você e seus clientes.

Você também pode verificar a sequência do modelo de hardware como "iPhone10,1", mas isso é problemático porque às vezes a Apple lança números de modelo diferentes para diferentes operadoras em todo o mundo.

A abordagem correta é redesenhar o layout superior ou resolver os problemas que você está tendo com a altura da barra de navegação personalizada (é nisso que eu focaria). Mas, se você decidir não fazer nenhuma dessas coisas, saiba que o que você está fazendo é um truque para que isso funcione hoje e precisará corrigi-lo em algum momento, talvez várias vezes, para manter os hacks trabalhando.

clarus
fonte
1
Certo. Refinar uma suposição de que o número X sempre será A para um número X sempre será A, a menos que a condição Y, quando será B, esteja apenas se aprofundando. Tamanho baseado na área segura indicada pela Apple, e não adivinhando.
Tommy
2
Vou me preocupar com o próximo iPhone quando ele estiver realmente disponível. Quero que meu aplicativo funcione HOJE.
Vahid Amiri
13

Resposta SWIFT 4+

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

Nota: Precisa de um dispositivo real para teste

Referência

 let deviceType = UIDevice.current.modelName
        switch deviceType {
        case "iPhone10,3", "iPhone10,6":
            print("iPhoneX")
        case "iPhone11,2":
            print("iPhone XS")
        case "iPhone11,4":
            print("iPhone XS Max")
        case "iPhone11,6":
            print("iPhone XS Max China")
        case "iPhone11,8":
            print("iPhone XR")
        case "iPhone12,3":
            print("iPhone 11 Pro")
        case "iPhone12,5":
            print("iPhone 11 Pro Max")
        default:
            break
}

extension UIDevice {
    var modelName: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8, value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }
        return identifier
    }
}
Jack
fonte
Para o método 1, você pode remover a propriedade "var window" fora da func e apenas uma constante "let" dentro dela (digite UIWindow, ou seja, não é opcional). Gosto dessa resposta, pois, na inicialização, o self.view.window pode ser nulo e o UIApplication.shared.keyWindow também pode ser nulo, enquanto a criação de um UIWindow dessa maneira funciona sempre.
Rolleric
11

Extensão reutilizável SWIFT 4/5 com suporte para iPhone 11

    public extension UIDevice {

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

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

    public var type: Type {
        if userInterfaceIdiom == .phone {
            switch UIScreen.main.nativeBounds.height {
            case 1136: return .iPhone_5_5S_5C
            case 1334: return .iPhone_6_6S_7_8
            case 1920, 2208: return .iPhone_6_6S_7_8_PLUS
            case 2436: return .iPhone_X_Xs
            case 2688: return .iPhone_Xs_11_Pro_Max
            case 1792: return .iPhone_Xr_11
            case 2426: return .iPhone_11_Pro
            default: return .iPhone_unknown
        }
        }
        return .iPad
   }
}
ale_stro
fonte
2
boa extensão, mas a mais útil aqui éUIDevice.current.hasHomeButton
WINSergey 28/11
1
@ale_stro é bom usar userInterfaceIdiom para determinar dispositivos para aplicativos universais? a maioria das pessoas não recomenda isso. existe algum mal em usá-lo?
Shaqir saiyed
10

Sim, é possível. Faça o download da extensão UIDevice-Hardware (ou instale via CocoaPod 'UIDevice-Hardware') e use:

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

Observe que isso não funcionará no Simulador, apenas no dispositivo real.

Hendrik
fonte
Todo o código do dispositivo aqui: iphonesoft.fr/2016/10/31/… Exemplo: iPhone X: iPhone10,5 e iPhone10,6
Medhi
As strings de hardware da wikipedia diziam "iPhone10,3 e iPhone10,6". @Medhi
Itachi
@ Medhi, você pode usar ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIF‌​IER"]no Simulator para obter os valores reais do Xcode.
Cœur
9

De acordo com a resposta do @ saswanb, esta é uma versão do Swift 4:

var iphoneX = false
if #available(iOS 11.0, *) {
    if ((UIApplication.shared.keyWindow?.safeAreaInsets.top)! > CGFloat(0.0)) {
        iphoneX = true
    }
}
MattOZ
fonte
A barra de status também é considerada fora da área segura! então isso retornará falsos positivos! Deve ser maior que 20 pontos (altura da barra de status). Isso também será verdadeiro se o dispositivo for iPhone Xs, R ou Xs Max.
MQoder 31/10/19
código funciona muito bem, mas tenha cuidado: keyWindowé nilaté o principal controlador de vista chamouviewDidAppear
Casey
9

Eu sei que é apenas uma solução Swift , mas poderia ajudar alguém.

Eu tenho globals.swiftem todos os projetos e uma das coisas que sempre adiciono é DeviceTypedetectar facilmente o dispositivo do usuário:

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

Então, para usá-lo:

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 você usar LaunchImageem seu projeto, adicione imagens para todos os dispositivos suportados (como XS Max, XR), pois UIScreen.main.boundsnão retornará o valor adequado sem eles.

budidino
fonte
1
Amigo novo para Swift perguntou como usar isso, apenas no caso de alguém não sabe ... if DeviceType.iPhoneX { //do something for iPhone X notch }else{ // don’t do anything about notch }
Liam Bolling
5

Todas as respostas que estão usando heightsão apenas metade da história por um motivo. Se você vai verificar assim quando a orientação do dispositivo estiver landscapeLeftou landscapeRighta verificação falhará, porque a heighttroca é feita com o width.

É por isso que minha solução se parece com isso no 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
fonte
Basta usar o nativeBounds
Leo Dabus
4

Você não deve presumir que o único dispositivo lançado pela Apple com uma altura diferente do UINavigationBar será o iPhone X. Tente resolver esse problema usando uma solução mais genérica. Se você deseja que a barra sempre seja 20px maior que a altura padrão, seu código deve adicionar 20px à altura da barra, em vez de defini-la para 64px (44px + 20px).

IMcD23
fonte
Então, que outra solução você tem a propor?
Stephane Mathis
@xaphod agora existem respostas melhores.
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
fonte
4

Rápido 3 + 4:

sem a necessidade de qualquer valor de pixel do tamanho do 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
    }
}

Exemplo:

if UIApplication.isDeviceWithSafeArea {
     //e.g. change the frame size height of your UITabBar
}
Peter Kreinz
fonte
3
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_X (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0f)
alexander.pan
fonte
2
ele irá retornar 812 se você padrão de carregamento de imagens para iPhone X. Até então eu acho que ele irá retornar o tamanho do iPhone 7, não tenho certeza embora ...
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;
    }
}
user6788419
fonte
1
Melhor resposta! Sem a necessidade de qualquer valor de pixel do tamanho do dispositivo.
Peter Kreinz
3

Geralmente, o programador precisa dele para restringir a parte superior ou inferior, para que esses métodos possam ajudar

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
}

Antes do iPhone X, esses métodos retornam: 0

Para o iPhone X: 44 e 34 em conformidade

Em seguida, basta adicionar esses extras às restrições superior ou inferior

Andrey
fonte
3

Para quem obtém 2001px em vez de 2436px para a altura dos limites nativos (como eu), é porque você criou seu aplicativo com um SDK mais antigo, antes do iOS 11 (Xcode 8 em vez do Xcode 9). Com um SDK mais antigo, o iOS exibirá os aplicativos com "caixa preta" no iPhone X, em vez de estender a tela de ponta a ponta, além do topo do "encaixe do sensor". Isso reduz o tamanho da tela e é por isso que essa propriedade retorna 2001 em vez de 2436.

A solução mais simples é apenas verificar os dois tamanhos, se você estiver interessado apenas na detecção de dispositivos. Eu usei esse método para detectar o FaceID ao criar com um Xcode SDK mais antigo que não possui o valor ENUM especificando o tipo biométrico. Nessa situação, a detecção do dispositivo usando a altura da tela parecia a melhor maneira de saber se o dispositivo tinha o FaceID vs TouchID sem precisar atualizar o Xcode.

Jon Summers
fonte
3

NÃO use o tamanho do pixel da tela, como sugeriram outras soluções; isso é ruim, pois pode resultar em falsos positivos para dispositivos futuros; não funcionará se o UIWindow ainda não tiver sido renderizado (AppDelegate), não funcionará em aplicativos de paisagem e poderá falhar no simulador se a escala estiver definida.

Em vez disso, criei uma macro para esse fim; é muito fácil de usar e depende de sinalizadores de hardware para evitar os problemas mencionados acima.

Editar: Atualizado para oferecer suporte ao iPhoneX, iPhone XS, iPhoneXR, iPhoneXS Max


Usar:

if (IS_DEVICE_IPHONEX) {
    //do stuff
}

Sim, sério.


Macro:

Basta copiar e colar isso em qualquer lugar, eu prefiro a parte inferior do meu arquivo .h depois @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
fonte
A única razão pela qual posso pensar em detectar o iPhoneX é evitar o entalhe na parte superior da tela; Nesse caso, você pode verificar o safeArea.top para detectar o tamanho do referido entalhe. Apenas certifique-se medi-la após UIWindow foi carregado, por isso não durante viewDidLoad mas um ciclo de rosca depois:if (@available(iOS 11.0, *)) { [UIApplication sharedApplication].keyWindow.safeAreaInsets.top }
Albert Renshaw
2

Eu elaborei as respostas dos outros e fiz uma extensão rápida no UIDevice. Eu gosto de enums rápidos e "tudo em ordem" e atomizado. Eu criei uma solução que funciona tanto no dispositivo quanto no simulador.

Vantagens: - interface simples, uso, por exemplo UIDevice.current.isIPhoneX - UIDeviceModelTypeenum permite ampliar facilmente recursos e constantes específicos do modelo que você deseja usar no seu aplicativo, por exemplo, cornerRadius

Desvantagem: - é uma solução específica do modelo, não específica da resolução - por exemplo, se a Apple produzir outro modelo com as mesmas especificações, isso não funcionará corretamente e você precisará adicionar outro modelo para fazer esse trabalho => você precisa atualizar seu aplicativo.

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
    }
}
cavalo da morte
fonte
Em vez de usar Mirror, será mais rápido usar sysctlbynamecomo feito na resposta do Cloud9999Strife (e na minha resposta também).
Cœur
2

Confio na altura do quadro da barra de status para detectar se é um iPhone X:

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

Isto é para aplicação em retrato. Você também pode verificar o tamanho de acordo com a orientação do dispositivo. Além disso, em outros iPhones, a Barra de status pode estar oculta, portanto a altura do quadro é 0. No iPhone X, a barra de status nunca fica oculta.

Tiois
fonte
Você pode ocultar a barra de status do iPhoneX controllercom isso: - (BOOL)prefersStatusBarHidden { return YES; } A altura da
barra de status
@ 无 夜 之 星辰 Verifico isso no momento da inicialização no AppDelegate.
Tiois 1/10
2

Eu estava usando o código de Peter Kreinz (porque estava limpo e fez o que eu precisava), mas então percebi que ele funciona exatamente quando o dispositivo está em retrato (pois o preenchimento superior estará no topo, obviamente). Então, criei uma extensão para lidar com todos os orientações com seus respectivos preenchimentos, sem retransmitir o tamanho da tela:

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 no seu site de chamadas, você apenas:

let res = UIDevice.current.isIphoneX
rgkobashi
fonte
2

Como alternativa, você pode conferir o pod ' DeviceKit '. Depois de instalado, tudo o que você precisa fazer para verificar o dispositivo é:

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

Nov 2019:

Aqui está o que eu uso em todos os meus projetos de produção. Note que esta essência é bastante longa.

  1. Isso não usa cálculos de largura ou altura, mas:
  2. Ele verifica o modelo de cadeia de dispositivos.
  3. Não corre o risco de rejeitar sua compilação pela Apple devido ao uso de APIs privadas / não documentadas.
  4. Funciona com simuladores

    import UIKit
    
    class DeviceUtility {
        /// Determines if the current device of the user is an iPhoneX type/variant.
        static var isIphoneXType: Bool {
            get {
                switch UIDevice().type {
                case .iPhoneXR, .iPhoneXS, .iPhoneXSMax, .iPhoneX, .iPhone11, .iPhone11Pro, .iPhone11ProMax: return true
                default: return false
                }
            }
        }
    }
    
    
    public enum DeviceModel : String {
        case simulator     = "simulator/sandbox",
    
        // MARK: - iPods
    
        iPod1              = "iPod 1",
        iPod2              = "iPod 2",
        iPod3              = "iPod 3",
        iPod4              = "iPod 4",
        iPod5              = "iPod 5",
    
        // MARK: - iPads
    
        iPad2              = "iPad 2",
        iPad3              = "iPad 3",
        iPad4              = "iPad 4",
        iPadAir            = "iPad Air ",
        iPadAir2           = "iPad Air 2",
        iPad5              = "iPad 5", //aka iPad 2017
        iPad6              = "iPad 6", //aka iPad 2018
    
        // MARK: - iPad Minis
    
        iPadMini           = "iPad Mini",
        iPadMini2          = "iPad Mini 2",
        iPadMini3          = "iPad Mini 3",
        iPadMini4          = "iPad Mini 4",
    
        // MARK: - iPad Pros
    
        iPadPro9_7         = "iPad Pro 9.7\"",
        iPadPro10_5        = "iPad Pro 10.5\"",
        iPadPro12_9        = "iPad Pro 12.9\"",
        iPadPro2_12_9      = "iPad Pro 2 12.9\"",
    
        // MARK: - iPhones
    
        iPhone4            = "iPhone 4",
        iPhone4S           = "iPhone 4S",
        iPhone5            = "iPhone 5",
        iPhone5S           = "iPhone 5S",
        iPhone5C           = "iPhone 5C",
        iPhone6            = "iPhone 6",
        iPhone6plus        = "iPhone 6 Plus",
        iPhone6S           = "iPhone 6S",
        iPhone6Splus       = "iPhone 6S Plus",
        iPhoneSE           = "iPhone SE",
        iPhone7            = "iPhone 7",
        iPhone7plus        = "iPhone 7 Plus",
        iPhone8            = "iPhone 8",
        iPhone8plus        = "iPhone 8 Plus",
        iPhoneX            = "iPhone X",
        iPhoneXS           = "iPhone XS",
        iPhoneXSMax        = "iPhone XS Max",
        iPhoneXR           = "iPhone XR",
        iPhone11           = "iPhone 11",
        iPhone11Pro        = "iPhone 11 Pro",
        iPhone11ProMax     = "iPhone 11 Pro Max",
    
        // MARK: - Apple TVs
    
        AppleTV            = "Apple TV",
        AppleTV_4K         = "Apple TV 4K",
    
        // MARK: - Unrecognized
    
        unrecognized       = "?unrecognized?"
    }
    
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    //MARK: UIDevice extensions
    // #-#-#-#-#-#-#-#-#-#-#-#-#-#-#
    
    public extension UIDevice {
        var type: DeviceModel {
            var systemInfo = utsname()
            uname(&systemInfo)
            let modelCode = withUnsafePointer(to: &systemInfo.machine) {
                $0.withMemoryRebound(to: CChar.self, capacity: 1) {
                    ptr in String.init(validatingUTF8: ptr)
    
                }
            }
            let modelMap : [ String : DeviceModel ] = [
    
                // MARK: - Simulators
    
                "i386"      : .simulator,
                "x86_64"    : .simulator,
    
                // MARK: - iPod
    
                "iPod1,1"   : .iPod1,
                "iPod2,1"   : .iPod2,
                "iPod3,1"   : .iPod3,
                "iPod4,1"   : .iPod4,
                "iPod5,1"   : .iPod5,
    
                // MARK: - iPad
    
                "iPad2,1"   : .iPad2,
                "iPad2,2"   : .iPad2,
                "iPad2,3"   : .iPad2,
                "iPad2,4"   : .iPad2,
                "iPad3,1"   : .iPad3,
                "iPad3,2"   : .iPad3,
                "iPad3,3"   : .iPad3,
                "iPad3,4"   : .iPad4,
                "iPad3,5"   : .iPad4,
                "iPad3,6"   : .iPad4,
                "iPad4,1"   : .iPadAir,
                "iPad4,2"   : .iPadAir,
                "iPad4,3"   : .iPadAir,
                "iPad5,3"   : .iPadAir2,
                "iPad5,4"   : .iPadAir2,
                "iPad6,11"  : .iPad5, //aka iPad 2017
                "iPad6,12"  : .iPad5,
                "iPad7,5"   : .iPad6, //aka iPad 2018
                "iPad7,6"   : .iPad6,
    
                // MARK: - iPad mini
    
                "iPad2,5"   : .iPadMini,
                "iPad2,6"   : .iPadMini,
                "iPad2,7"   : .iPadMini,
                "iPad4,4"   : .iPadMini2,
                "iPad4,5"   : .iPadMini2,
                "iPad4,6"   : .iPadMini2,
                "iPad4,7"   : .iPadMini3,
                "iPad4,8"   : .iPadMini3,
                "iPad4,9"   : .iPadMini3,
                "iPad5,1"   : .iPadMini4,
                "iPad5,2"   : .iPadMini4,
    
                // MARK: - iPad pro
    
                "iPad6,3"   : .iPadPro9_7,
                "iPad6,4"   : .iPadPro9_7,
                "iPad7,3"   : .iPadPro10_5,
                "iPad7,4"   : .iPadPro10_5,
                "iPad6,7"   : .iPadPro12_9,
                "iPad6,8"   : .iPadPro12_9,
                "iPad7,1"   : .iPadPro2_12_9,
                "iPad7,2"   : .iPadPro2_12_9,
    
                // MARK: - iPhone
    
                "iPhone3,1" : .iPhone4,
                "iPhone3,2" : .iPhone4,
                "iPhone3,3" : .iPhone4,
                "iPhone4,1" : .iPhone4S,
                "iPhone5,1" : .iPhone5,
                "iPhone5,2" : .iPhone5,
                "iPhone5,3" : .iPhone5C,
                "iPhone5,4" : .iPhone5C,
                "iPhone6,1" : .iPhone5S,
                "iPhone6,2" : .iPhone5S,
                "iPhone7,1" : .iPhone6plus,
                "iPhone7,2" : .iPhone6,
                "iPhone8,1" : .iPhone6S,
                "iPhone8,2" : .iPhone6Splus,
                "iPhone8,4" : .iPhoneSE,
                "iPhone9,1" : .iPhone7,
                "iPhone9,3" : .iPhone7,
                "iPhone9,2" : .iPhone7plus,
                "iPhone9,4" : .iPhone7plus,
                "iPhone10,1" : .iPhone8,
                "iPhone10,4" : .iPhone8,
                "iPhone10,2" : .iPhone8plus,
                "iPhone10,5" : .iPhone8plus,
                "iPhone10,3" : .iPhoneX,
                "iPhone10,6" : .iPhoneX,
                "iPhone11,2" : .iPhoneXS,
                "iPhone11,4" : .iPhoneXSMax,
                "iPhone11,6" : .iPhoneXSMax,
                "iPhone11,8" : .iPhoneXR,
                "iPhone12,1" : .iPhone11,
                "iPhone12,3" : .iPhone11Pro,
                "iPhone12,5" : .iPhone11ProMax,
    
                // MARK: - AppleTV
    
                "AppleTV5,3" : .AppleTV,
                "AppleTV6,2" : .AppleTV_4K
            ]
    
            if let model = modelMap[String.init(validatingUTF8: modelCode!)!] {
                if model == .simulator {
                    if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
                        if let simModel = modelMap[String.init(validatingUTF8: simModelCode)!] {
                            return simModel
                        }
                    }
                }
                return model
            }
            return DeviceModel.unrecognized
        }
    }

Uso: deixe inserir: CGFloat = DeviceUtility.isIphoneXType? 50.0: 40.0

Glenn
fonte
Funciona perfeitamente. Obrigado. Estou usando-o em um projeto SwiftUI.
usar o seguinte código
1

Eu tive que resolver o mesmo problema recentemente. E embora essa pergunta seja definitivamente respondida ("Não"), isso pode ajudar outras pessoas que precisam de um comportamento de layout específico do iPhone X.

Eu não estava realmente interessado em saber se o dispositivo era o iPhone X. Eu estava interessado em saber se o dispositivo tinha uma tela entalhada.

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
}

Você também pode escrever uma hasOnScreenHomeIndicatorvariável nas mesmas linhas (embora verifique a área inferior segura, talvez?).

O exemplo acima usa minha extensão UIViewpara acesso conveniente às inserções da área segura no iOS 10 e versões anteriores.

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

    @objc public var compatibleSafeAreaLayoutGuide: UILayoutGuide {
        if #available(iOS 11.0, *) {
            return safeAreaLayoutGuide
        } else {
            return layoutMarginsGuide
        }
    }
}
simeon
fonte
1

Em Retrato única Eu uso largura e altura do quadro do objectivo de verificar:

override func viewDidLoad() {
    super.viewDidLoad()

    // iPhone Xr: -414 x 896
    // iPhone Xs Max: -414 x 896
    // iPhone X, Xs: -375 x 812

    if view.frame.width == 414 && view.frame.height == 896 || view.frame.width == 375 && view.frame.height == 812  {

        print("iPhone X")
    } else {

        print("not iPhone X")
    }

}

As dimensões da tela de retrato estão listadas aqui

insira a descrição da imagem aqui

Lance Samaria
fonte
0

Há vários motivos para querer saber qual é o dispositivo.

  1. Você pode verificar a altura (e a largura) do dispositivo. Isso é útil para o layout, mas você geralmente não deseja fazer isso se quiser conhecer o dispositivo exato.

  2. Para fins de layout, você também pode usar UIView.safeAreaInsets.

  3. Se você deseja exibir o nome do dispositivo, por exemplo, para ser incluído em um email para fins de diagnóstico, após recuperar o modelo do dispositivo sysctl (), use o equivalente a isso para descobrir o nome:

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