Detectar a tela da retina


223

O SDK do iOS fornece uma maneira fácil de verificar se o dispositivo atual possui uma tela de alta resolução (retina)?

A melhor maneira que encontrei para fazer isso agora é:

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) {
         // RETINA DISPLAY
    }

Por curiosidade - o que você está fazendo quando detecta a exibição, além de mostrar versões maiores de sua obra de arte?
Michael Behan


@mbehan: Eu tenho um TTImageView (consulte a estrutura Three20) e quero fornecer um URL de alta resolução da imagem.
Pierre Valade

1
Esta pergunta também é útil para mim, porque eu baixei imagens que apresentam como interface do usuário disponíveis em tamanhos para todos os 4 tamanhos de tela e só quero que os usuários baixem o apropriado.
Pedro

@mbehan: no meu caso, eu queria separadores de células personalizados com 1px nas telas retina e não retina (como os separadores nativos). Definir a espessura para 1px é renderizado em 2px em telas de retina (obviamente).
user3099609

Respostas:


295

Para detectar a exibição Retina de maneira confiável em todos os dispositivos iOS, é necessário verificar se o dispositivo está executando o iOS4 + e se a [UIScreen mainScreen].scalepropriedade é igual a 2.0. Você NÃO PODE assumir que um dispositivo está executando o iOS4 + se a scalepropriedade existir, pois o iPad 3.2 também contém essa propriedade.

Em um iPad executando o iOS3.2, a escala retornará 1.0 no modo 1x e 2.0 no modo 2x - mesmo sabendo que o dispositivo não contém uma tela Retina. A Apple alterou esse comportamento no iOS4.2 para o iPad: ele retorna 1.0 nos modos 1x e 2x. Você pode testar isso sozinho no simulador.

-displayLinkWithTarget:selector:Testo o método na tela principal existente no iOS4.x, mas não no iOS3.2, e depois verifico a escala da tela:

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0)) {
  // Retina display
} else {
  // non-Retina display
}

Você diz que "a Apple alterou esse comportamento no iOS4.2 para iPad", o que implica que no iOS4.1 o código acima retornaria "é Retina" para um iPad executando um aplicativo para iPhone no modo 2x. Estou errado?
makdad

9
Nunca houve um 4.1 para iPad. Apenas 3.2, depois 4.2.
Anónimo

11
Essa chamada é um pouco cara, então eu inicializaria um BOOL com ele no início do aplicativo e o usaria no aplicativo.
n13 20/05/12

Eu prefiro verificar a versão usando [UIDevice currentDevice].systemVersion]. Nesse caso, seria NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman

Não parece funcionar no simulador para iPad sem retina (iOS 7.1) no xcode 4 ... estranho.
Isaac Paul

81

A resposta de @ sickp está correta. Apenas para facilitar as coisas, adicione esta linha ao seu arquivo Shared.pch:

#define IS_RETINA ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale >= 2.0))

Em qualquer arquivo, você pode simplesmente fazer:

if(IS_RETINA)
{
   // etc..
}

Isso não funciona no simulador. É por causa do respondsToSelector? O simulador não responde ao seletor?
Arniotaki

2
Ótimo! No entanto, se você quiser levar em conta o iPhone 6 Plus, verifique a escala> = 2,0.
Ivan Carosati

20
+(BOOL)iPhoneRetina{
    return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?1:0;
}

23
Por que o ?1:0? Isso não é apenas reiterar o que já foi calculado na parte booleana da expressão?
D11wtq

9

Aqui está uma extensão rápida e prática:

Atualização para o Swift v5:

extension UIScreen {

    public var isRetina: Bool {
        guard let scale = screenScale else {
            return false
        }
        return scale >= 2.0
    }

    public var isRetinaHD: Bool {
        guard let scale = screenScale else {
            return false
        }
        return scale >= 3.0
    }

    private var screenScale: CGFloat? {
        guard UIScreen.main.responds(to: #selector(getter: scale)) else {
            return nil
        }
        return UIScreen.main.scale
    }
}

Uso:

if UIScreen.main.isRetina {
    // Your code
}

Original:

extension UIScreen { 
public func isRetina() -> Bool {
    return screenScale() >= 2.0
}

public func isRetinaHD() -> Bool {
    return screenScale() >= 3.0
}

private func screenScale() -> CGFloat? {
    if UIScreen.mainScreen().respondsToSelector(Selector("scale")) {
        return UIScreen.mainScreen().scale
    }
    return nil
    }
}

Uso:

if UIScreen.mainScreen().isRetina() {
 // your code
        }

O código que é atualizado para funcionar no Swift 5 não deve verificar se iscreenScale é> = 3.0 e não 2.0 ??? Edit: Eu atualizei ...
C0D3 12/03

6

Este trecho ...

    int d = 0; // standard display
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0) {
    d = 1; // is retina display
}

if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    d += 2;
}

Retornará ... 0 para iPhone / iPod touch com resolução padrão, 1 para iPhone com retina, 2 para iPad com resolução padrão, 3 para iPad com retina.



5

Sempre é um pouco complicado comparar valores de ponto flutuante para igualdade. Eu prefiro ir para qualquer um

[UIScreen mainScreen].scale > 1.0;

ou

[UIScreen mainScreen].scale < 2.0;

5
Comparar dois valores de ponto flutuante para igualdade "parece desonesto", porque eles podem diferir ligeiramente dos valores integrais após os cálculos. Mas comparar com <ou> é tão desonesto nessas situações. Nesse caso, no entanto, não há chance de que essa escala não seja exatamente 1.0 ou 2.0, pois é definida por hardware.
fishinperto de 22/03/2013

Como o @fishinear sugere, é melhor usar algo parecido isRetina = [UIScreen mainScreen].scale > 1.95. Isso também terá a vantagem de ser resistente a quando @ 4x vem junto :)
Danyal Aytekin

Eu discordo fortemente. Fazer isso quando não for necessário torna o código menos legível. O ponto sobre a possibilidade de futuro pode ter sua validade, mas duvido que teremos telas @ 4x em breve (se houver).
Ricardo Sanchez-Saez

Errado. O fato de ser "definido por hardware" não significa, de forma alguma, que você evite o problema de comparar um flutuador. (É apenas um float como qualquer outro.) Como em qualquer float, em geral você nunca pode usar ==, deve usar uma comparação> ou <. Que tal> 1,5 para garantir.
Fattie

2

Este é um riff na resposta de Matt MC acima. Apenas uma categoria ativada UIScreen.

#import "UIScreen+Util.h"

@implementation UIScreen (Util)

+ (BOOL) isRetinaDisplay {
    static BOOL retina = NO;
    static BOOL alreadyChecked = NO;
    if (!alreadyChecked) {
        UIScreen *mainScreen = self.mainScreen;
        if (mainScreen) {
            retina = mainScreen.scale > 1.0;
            alreadyChecked = YES;
        }
    }
    return retina;
}

@end

1
Suspeito que o armazenamento em cache alreadyCheckedseja gratuito, mas tudo bem.
Dan Rosenstark 28/11

@NikolayShubenkov é por isso que eu já defini o último check. Na pior das hipóteses, você executa o código para verificar um tempo ou dois extras.
precisa saber é o seguinte

Quero dizer, quando um processo tentará jáChecked enquanto outro está lendo este aplicativo de valor pode falhar. Gostaria de acrescentar que a linha: @synchronyze (alreadyChecked) {alreadyChecked = YES}
Nikolay Shubenkov

2

Versão rápida das respostas acima, com escala> = 2.0, para incluir o iPhone 6+ e outros dispositivos futuros com escala maior que a Retina:

 if UIScreen.mainScreen().respondsToSelector(Selector("scale")) && UIScreen.mainScreen().scale >= 2.0 {
    // code executed only on Retina device
}

1

Apenas para combinar a resposta de @sickp e o seguinte comentário de @ n13, eu fiz isso em uma categoria UIScreen que parece funcionar bem. A verificação é feita na primeira vez em que você liga e, em seguida, salva para chamadas posteriores.

@interface UIScreen (RetinaCheck)
+ (BOOL)retinaScreen;
@end

static BOOL isRetinaScreen = NO;
static BOOL didRetinaCheck = NO;

@implementation UIScreen (RetinaCheck)
+ (BOOL)retinaScreen
{
    if (!didRetinaCheck) {
        isRetinaScreen = ([[self mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
                          ([self mainScreen].scale == 2.0));
        didRetinaCheck = YES;
    }
    return isRetinaScreen;
}
@end

Pode ser útil para alguém.


Obrigado pelo código de cache. Minha única sugestão é fazer isso em (Util)vez de (RetinaCheck)... talvez seja menos claro, mas se presta a outros usos. Também eu nomearia o método isRetinaDisplayou algo que começa com is, mas talvez nunca tenha entendido as diretrizes do Obj-C. Além disso, sou fã, > 1.0mas quem sabe o que fará sentido no futuro.
Dan Rosenstark

1
// .h
UIKIT_EXTERN bool isRetinaDisplay();

// .m
bool isRetinaDisplay()
{
    static bool flag;
#ifdef __BLOCKS__
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        {
            flag = [[UIScreen mainScreen] scale] > 1.0;
        }
        else
        {
            flag = false;
        }
    });
#else
    static bool onceToken;
    if(onceToken == false)
    {
        onceToken = true;
        if([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        {
            flag = [[UIScreen mainScreen] scale] > 1.0;
        }
        else
        {
            flag = false;
        }
    }
#endif
    return flag;
}

Melhor solução como eu penso.
Nikolay Shubenkov

0

tente isso

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0))
{
    // Retina display
    NSLog(@"---------------Retina display");
} else {
    // non-Retina display
    NSLog(@"---------------non-Retina display");
}

0

Versão modificada do primulaveris para simplificar os casos de uso mais comuns. Estou no veloz 2.2, mas isso não deve importar.

extension UIScreen {
    static var isRetina: Bool {
        return screenScale >= 2.0
    }

    static var isRetinaHD: Bool {
        return screenScale >= 3.0
    }

    static var screenScale:CGFloat {
        return UIScreen.mainScreen().scale
    }
}

Em seguida, basta usá-los assim

print(UIScreen.isRetina)
print(UIScreen.isRetinaHD)
print(UIScreen.screenScale)

0

Isso funcionou para mim

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.