Detectar pantalla Retina


223

¿IOS SDK proporciona una manera fácil de verificar si el dispositivo actual tiene una pantalla de alta resolución (retina)?

La mejor manera que he encontrado para hacerlo ahora es:

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

Por curiosidad, ¿qué haces cuando detectas la pantalla además de mostrar versiones más grandes de tu obra de arte?
Michael Behan


@mbehan: Tengo un TTImageView (ver Three20 framework) y quiero dar una URL de alta resolución de la imagen.
Pierre Valade

1
Esta pregunta también me resulta útil porque he descargado imágenes que se presentan como IU disponibles en tamaños para los 4 tamaños de pantalla y solo quiero que los usuarios descarguen la adecuada.
Pedro

@mbehan: en mi caso, quería separadores de celdas personalizados que son 1px en pantallas retina y no retina (como los separadores nativos). Establecer el grosor en 1px se procesa en 2px en pantallas de retina (obviamente).
user3099609

Respuestas:


295

Para detectar la pantalla Retina de manera confiable en todos los dispositivos iOS, debe verificar si el dispositivo ejecuta iOS4 + y si la [UIScreen mainScreen].scalepropiedad es igual a 2.0. NO PUEDE asumir que un dispositivo está ejecutando iOS4 + si la scalepropiedad existe, ya que el iPad 3.2 también contiene esta propiedad.

En un iPad con iOS3.2, la escala devolverá 1.0 en modo 1x y 2.0 en modo 2x, aunque sabemos que el dispositivo no contiene una pantalla Retina. Apple cambió este comportamiento en iOS4.2 para iPad: devuelve 1.0 en los modos 1x y 2x. Puede probar esto usted mismo en el simulador.

-displayLinkWithTarget:selector:Pruebo el método en la pantalla principal que existe en iOS4.x pero no en iOS3.2, y luego verifico la escala de la pantalla:

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

Usted dice que "Apple cambió este comportamiento en iOS4.2 para el iPad", lo que implica que en iOS4.1, su código anterior devolvería "es Retina" para un iPad que ejecuta una aplicación de iPhone en modo 2x. ¿Me equivoco?
makdad

99
Nunca hubo un 4.1 para iPad. Solo 3.2, luego 4.2.
Jonny

11
Esta llamada es un poco costosa, por lo que inicializaría un BOOL con ella en el inicio de la aplicación y la usaría en la aplicación.
n13

Prefiero verificar la versión usando [UIDevice currentDevice].systemVersion]. En este caso sería NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman el

No parece funcionar en el simulador para iPad sin retina (ios 7.1) en xcode 4 ... raro.
Isaac Paul

81

La respuesta de @ sickp es correcta. Solo para facilitar las cosas, agregue esta línea en su archivo Shared.pch:

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

Luego, en cualquier archivo, simplemente puede hacer:

if(IS_RETINA)
{
   // etc..
}

Esto no funciona en el simulador. ¿Es por el respondsToSelector? El simulador no responde al selector?
arniotaki

2
¡Excelente! Sin embargo, si desea tener en cuenta el iPhone 6 Plus, debe verificar la escala> = 2.0.
Ivan Carosati

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

23
¿Por qué el ?1:0? ¿No es eso solo reiterar lo que ya se ha calculado en la parte booleana de la expresión?
d11wtq

9

Aquí hay una práctica extensión rápida:

Actualización para 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
        }

¿No debería el código que se actualiza para funcionar para Swift 5 isRetinaHD verificar si iscreenScale es> = 3.0 no 2.0? Editar: Lo actualicé ...
C0D3

6

Este fragmento ...

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

Volverá ... 0 para iPhone / iPod touch de resolución estándar, 1 para iPhone de retina, 2 para iPad de resolución estándar, 3 para iPad de retina.



5

Siempre se siente un poco dudoso comparar los valores de coma flotante para la igualdad. Prefiero ir por cualquiera

[UIScreen mainScreen].scale > 1.0;

o

[UIScreen mainScreen].scale < 2.0;

55
Comparar dos valores de coma flotante para la igualdad "se siente dudoso", ya que pueden diferir ligeramente de los valores integrales después de los cálculos. Pero comparar con <o> es igual de dudoso en esas situaciones. En este caso, sin embargo, no hay ninguna posibilidad de que esa escala no sea exactamente 1.0 o 2.0, ya que está definida por hardware.
fishinear

Como sugiere @fishinear, es mejor usar algo como isRetina = [UIScreen mainScreen].scale > 1.95. Esto también tendrá el beneficio de ser resistente cuando aparezca @ 4x :)
Danyal Aytekin

Estoy totalmente en desacuerdo. Hacer esto cuando no sea necesario hace que el código sea menos legible. El punto sobre la prueba de futuro podría tener validez, pero dudo que tengamos pantallas @ 4x en el corto plazo (si es que lo hay).
Ricardo Sanchez-Saez

Incorrecto. el hecho de que esté "definido por hardware" no significa, de ninguna manera, que evite el problema de comparar un flotante. (Es solo un flotador como cualquier otro). Al igual que con cualquier flotador, en general nunca puede usar ==, debe usar una comparación> o <. ¿Qué pasa con> 1.5 para tener certeza?
Fattie

2

Este es un riff en la respuesta de Matt MC anterior. Solo una categoría 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
Sospecho que el almacenamiento en caché alreadyCheckedes gratuito, pero está bien.
Dan Rosenstark

@NikolayShubenkov es por eso que configuré el último. En el peor de los casos, ejecuta el código para verificar un tiempo extra o dos.
Dan Rosenstark

Quiero decir, cuando un proceso intentará comprobarse mientras otro está leyendo este valor, la aplicación puede fallar. Agregaría esa línea: @synchronyze (alreadyChecked) {alreadyChecked = YES}
Nikolay Shubenkov

2

Versión rápida de las respuestas anteriores, con una escala> = 2.0, por lo que incluye iPhone 6+ y otros dispositivos futuros con una escala superior a Retina:

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

1

Solo para combinar la respuesta de @sickp y el siguiente comentario de @ n13, convertí esto en una categoría UIScreen que parece funcionar bien. La verificación se realiza la primera vez que la llama y luego se guarda para llamadas 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

Puede ser útil para alguien.


Gracias por el código de caché. Mi única sugerencia es hacer esto en (Util)lugar de (RetinaCheck)... tal vez sea menos claro, pero se presta para otros usos. También nombraría el método isRetinaDisplayo algo que comience is, pero tal vez nunca entendí las pautas para Obj-C. Además, soy fanático, > 1.0pero quién sabe qué tendrá sentido seguir adelante.
Dan Rosenstark el

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

La mejor solución como creo.
Nikolay Shubenkov

0

prueba esto

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

Versión modificada de primulaveris para simplificar los casos de uso más comunes. Estoy en Swift 2.2 pero no debería 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
    }
}

Entonces simplemente utilízalos así

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

0

Esto funciono para mi

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.