Rileva display Retina


223

IOS SDK offre un modo semplice per verificare se currentDevice ha un display ad alta risoluzione (retina)?

Il modo migliore che ho trovato per farlo ora è:

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

Per curiosità: cosa fai quando rilevi il display oltre a mostrare versioni più grandi della tua opera d'arte?
Michael Behan,


@mbehan: ho un TTImageView (vedi il framework Three20) e voglio dare un url ad alta risoluzione dell'immagine.
Pierre Valade,

1
Questa domanda mi è utile anche perché ho scaricato immagini che presentano come interfaccia utente disponibili nelle dimensioni per tutte e 4 le dimensioni dello schermo e voglio solo che gli utenti scarichino quello appropriato.
Pedro,

@mbehan: nel mio caso volevo separatori di celle personalizzati che sono 1px su schermi retina e non retina (come i separatori nativi). L'impostazione dello spessore su 1px viene eseguito il rendering a 2px sui display retina (ovviamente).
user3099609,

Risposte:


295

Al fine di rilevare il display Retina in modo affidabile su tutti i dispositivi iOS, è necessario verificare se il dispositivo esegue iOS4 + e se la [UIScreen mainScreen].scaleproprietà è uguale a 2.0. NON PUOI presumere che un dispositivo esegua iOS4 + se la scaleproprietà esiste, poiché anche iPad 3.2 contiene questa proprietà.

Su un iPad con iOS3.2, la scala restituirà 1.0 in modalità 1x e 2.0 in modalità 2x, anche se sappiamo che il dispositivo non contiene un display Retina. Apple ha modificato questo comportamento in iOS4.2 per iPad: restituisce 1.0 in entrambe le modalità 1x e 2x. Puoi testarlo tu stesso nel simulatore.

Cerco il -displayLinkWithTarget:selector:metodo sulla schermata principale che esiste in iOS4.x ma non iOS3.2, quindi controllo la scala dello schermo:

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

Dici che "Apple ha modificato questo comportamento in iOS4.2 per iPad", sottintendendo che in iOS4.1, il codice sopra riportato restituisce "is Retina" per un iPad che esegue un'app per iPhone in modalità 2x. Ho sbagliato?
Makkad,

9
Non c'è mai stato un 4.1 per iPad. Solo 3.2, quindi 4.2.
Jonny

11
Questa chiamata è un po 'costosa, quindi inizializzare un BOOL con esso all'avvio dell'app e utilizzarlo nell'app.
13

Preferisco controllare la versione usando [UIDevice currentDevice].systemVersion]. In questo caso sarebbe NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman il

Non sembra funzionare nel simulatore per iPad non retina (iOS 7.1) in xcode 4 ... strano.
Isaac Paul,

81

La risposta di @ sickp è corretta. Per semplificare le cose, aggiungi questa riga nel tuo file Shared.pch:

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

Quindi in qualsiasi file puoi semplicemente fare:

if(IS_RETINA)
{
   // etc..
}

Questo non funziona sul simulatore. È a causa di respondsToSelector? Il simulatore non risponde al selettore?
Arniotaki,

2
Grande! Tuttavia, se si desidera prendere in considerazione l'iPhone 6 Plus, è necessario controllare la scala> = 2.0.
Ivan Carosati,

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

23
Perché il ?1:0? Non è solo reiterare ciò che è già stato calcolato nella parte booleana dell'espressione?
d11wtq,

9

Ecco una comoda estensione rapida:

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

Originale:

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
        }

Il codice aggiornato per funzionare con Swift 5 isRetinaHD dovrebbe verificare se iscreenScale è> = 3.0 non 2.0 ??? Modifica: l'ho aggiornato ...
C0D3

6

Questo frammento ...

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

Restituirà ... 0 per iPhone / iPod touch con risoluzione standard, 1 per iPhone con retina, 2 per iPad con risoluzione standard, 3 per iPad con retina.



5

È sempre un po 'complicato confrontare i valori in virgola mobile per l'uguaglianza. Preferisco andare per entrambi

[UIScreen mainScreen].scale > 1.0;

o

[UIScreen mainScreen].scale < 2.0;

5
Il confronto di due valori in virgola mobile per l'uguaglianza "sembra complicato", poiché dopo i calcoli possono differire leggermente dai valori integrali. Ma il confronto con <o> è altrettanto complicato in quelle situazioni. In questo caso, tuttavia, non vi è alcuna possibilità che la scala non sia esattamente 1.0 o 2.0, poiché è definita dall'hardware.
Fishinear dal

Come suggerisce @fishinear, meglio usare qualcosa di simile isRetina = [UIScreen mainScreen].scale > 1.95. Ciò avrà anche il vantaggio di essere resiliente quando arriva @ 4x :)
Danyal Aytekin,

Sono fortemente in disaccordo. Fare questo quando non necessario rende il codice meno leggibile. Il punto sulla sicurezza del futuro potrebbe avere la sua validità, ma dubito che avremo presto schermi @ 4x (se non del tutto).
Ricardo Sanchez-Saez,

Sbagliato. solo perché è "definito dall'hardware" non significa, in alcun modo, evitare il problema del confronto-a-float. (È solo un float come un altro.) Come con qualsiasi float, in generale non puoi mai usare ==, devi usare un confronto> o <. Che dire> 1,5 per certezza.
Fattie,

2

Questo è un riff sulla risposta di Matt MC sopra. Solo una categoria 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
Sospetto che la memorizzazione nella cache di alreadyCheckedsia gratuita, ma va bene.
Dan Rosenstark,

@NikolayShubenkov è per questo che ho impostato già verificato per ultimo. Nel peggiore dei casi, si esegue il codice per controllare uno o due tempi supplementari.
Dan Rosenstark,

Intendo quando un processo proverà già a verificare mentre altri stanno leggendo questa app di valore potrebbe bloccarsi. Vorrei aggiungere quella riga: @synchronyze (già verificato) {già verificato = SÌ}
Nikolay Shubenkov

2

Versione rapida delle risposte sopra, con scala = = 2.0, quindi include iPhone 6+ e altri dispositivi futuri con scala superiore alla Retina:

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

1

Solo per combinare la risposta di @sickp e il seguente commento di @ n13 l'ho trasformato in una categoria UIScreen che sembra funzionare bene. Il controllo viene eseguito la prima volta che lo si chiama e quindi viene salvato per le chiamate successive.

@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

Potrebbe essere utile a qualcuno.


Grazie per il codice di memorizzazione nella cache. Il mio unico suggerimento è quello di rendere questo (Util)invece di (RetinaCheck)... forse è meno chiaro, ma si presta ad altri usi. Inoltre vorrei nominare il metodo isRetinaDisplayo qualcosa che inizia con is, ma forse non ho mai capito le linee guida per Obj-C. Inoltre, sono un fan di, > 1.0ma chissà cosa avrà senso andare avanti.
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;
}

La migliore soluzione come penso.
Nikolay Shubenkov,

0

prova questo

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

Versione modificata di primulaveris per semplicità dei casi d'uso più comuni. Sono su 2.2 veloce ma non dovrebbe importare.

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

Quindi semplicemente usali in questo modo

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

0

Questo ha funzionato per me

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.