Netzhautanzeige erkennen


223

Bietet das iOS SDK eine einfache Möglichkeit, um zu überprüfen, ob das aktuelle Gerät über ein hochauflösendes Display (Retina) verfügt?

Der beste Weg, den ich jetzt gefunden habe, ist:

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

Aus Neugier - was machen Sie, wenn Sie das Display erkennen, außer größere Versionen Ihres Kunstwerks zu zeigen?
Michael Behan


@mbehan: Ich habe ein TTImageView (siehe Three20-Framework) und möchte eine hochauflösende URL des Bildes angeben.
Pierre Valade

1
Diese Frage ist auch für mich nützlich, da ich Bilder heruntergeladen habe, die als Benutzeroberfläche in Größen für alle 4 Anzeigegrößen verfügbar sind, und nur Benutzer die entsprechenden herunterladen lassen möchte.
Pedro

@mbehan: In meinem Fall wollte ich benutzerdefinierte Zellentrennzeichen, die sowohl auf Retina- als auch auf Nicht-Retina-Bildschirmen 1 Pixel groß sind (wie die nativen Separatoren). Wenn Sie die Dicke auf 1 Pixel einstellen, wird auf Retina-Displays (offensichtlich) 2 Pixel angezeigt.
user3099609

Antworten:


295

Um die Retina-Anzeige auf allen iOS-Geräten zuverlässig zu erkennen, müssen Sie überprüfen, ob auf dem Gerät iOS4 + ausgeführt wird und ob die [UIScreen mainScreen].scaleEigenschaft gleich 2.0 ist. Sie können NICHT davon ausgehen, dass auf einem Gerät iOS4 + ausgeführt wird, wenn die scaleEigenschaft vorhanden ist, da das iPad 3.2 diese Eigenschaft ebenfalls enthält.

Auf einem iPad mit iOS3.2 gibt die Skalierung im 1x-Modus 1.0 und im 2x-Modus 2.0 zurück - obwohl wir wissen, dass das Gerät kein Retina-Display enthält. Apple hat dieses Verhalten in iOS4.2 für das iPad geändert: Es gibt 1.0 sowohl im 1x- als auch im 2x-Modus zurück. Sie können dies selbst im Simulator testen.

Ich -displayLinkWithTarget:selector:teste die Methode auf dem Hauptbildschirm, der in iOS4.x, aber nicht in iOS3.2 vorhanden ist, und überprüfe dann die Skalierung des Bildschirms:

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

Sie sagen, dass "Apple dieses Verhalten in iOS4.2 für das iPad geändert hat", was bedeutet, dass in iOS4.1 Ihr obiger Code "ist Retina" für ein iPad zurückgibt, auf dem eine iPhone-App im 2x-Modus ausgeführt wird. Liege ich falsch?
Makdad

9
Es gab nie eine 4.1 für das iPad. Nur 3.2, dann 4.2.
Jonny

11
Dieser Aufruf ist etwas teuer, daher würde ich beim Start der App ein BOOL damit initialisieren und in der App verwenden.
n13

Ich bevorzuge es, die Version mit zu überprüfen [UIDevice currentDevice].systemVersion]. In diesem Fall wäre es NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman

Scheint im Simulator für iPad ohne Netzhaut (ios 7.1) in xcode 4 nicht zu funktionieren ... seltsam.
Isaac Paul

81

Die Antwort von @ sickp ist richtig. Fügen Sie diese Zeile zur Vereinfachung in Ihre Shared.pch-Datei ein:

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

Dann können Sie in jeder Datei einfach Folgendes tun:

if(IS_RETINA)
{
   // etc..
}

Dies funktioniert nicht im Simulator. Liegt es am responsondsToSelector? Simulator reagiert nicht auf Selektor?
Arniotaki

2
Toll! Wenn Sie jedoch das iPhone 6 Plus berücksichtigen möchten, sollten Sie nach Skalierung> = 2,0 suchen.
Ivan Carosati

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

23
Warum das ?1:0? Wiederholt das nicht nur das, was bereits im booleschen Teil des Ausdrucks berechnet wurde?
d11wtq

9

Hier ist eine praktische schnelle Erweiterung:

Update für 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
    }
}

Verwendung:

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

Verwendung:

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

Sollte der Code, der für Swift 5 isRetinaHD aktualisiert wurde, nicht prüfen, ob iscreenScale> = 3.0 ist, nicht 2.0 ??? Bearbeiten: Ich habe es aktualisiert ...
C0D3

6

Dieser Ausschnitt ...

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

Wird zurückgegeben ... 0 für iPhone / iPod touch mit Standardauflösung, 1 für iPhone mit Netzhaut, 2 für iPad mit Standardauflösung, 3 für iPad mit Netzhaut.



5

Es ist immer etwas zwielichtig, Gleitkommawerte auf Gleichheit zu vergleichen. Ich gehe lieber für beides

[UIScreen mainScreen].scale > 1.0;

oder

[UIScreen mainScreen].scale < 2.0;

5
Der Vergleich zweier Gleitkommawerte für die Gleichheit "fühlt sich zwielichtig an", da sie sich nach Berechnungen geringfügig von den Integralwerten unterscheiden können. Aber der Vergleich mit <oder> ist in solchen Situationen genauso zweifelhaft. In diesem Fall besteht jedoch überhaupt keine Chance, dass der Maßstab nicht genau 1,0 oder 2,0 beträgt, da er hardwaredefiniert ist.
fishinear

Wie @fishinear vorschlägt, ist es besser, so etwas wie zu verwenden isRetina = [UIScreen mainScreen].scale > 1.95. Dies wird auch den Vorteil haben, widerstandsfähig zu sein, wenn @ 4x kommt :)
Danyal Aytekin

Ich bin absolut anderer Meinung. Wenn Sie dies nicht benötigen, ist der Code weniger lesbar. Der Punkt der Zukunftssicherheit mag seine Gültigkeit haben, aber ich bezweifle, dass wir bald (wenn überhaupt) @ 4x-Bildschirme haben werden.
Ricardo Sanchez-Saez

Falsch. Nur weil es "Hardware-definiert" ist, bedeutet dies in keiner Weise, dass Sie das Problem des Vergleichs eines Floats vermeiden. (Es ist nur ein Float wie jeder andere.) Wie bei jedem Float können Sie im Allgemeinen niemals == verwenden, sondern müssen einen> oder <Vergleich verwenden. Was ist mit> 1,5 für die Sicherheit.
Fattie

2

Dies ist ein Riff zu Matt MCs Antwort oben. Nur eine Kategorie auf 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
Ich vermute, dass das Caching von alreadyCheckedkostenlos ist, aber es ist in Ordnung.
Dan Rosenstark

@NikolayShubenkov deshalb habe ich schonChecked zuletzt eingestellt. Im schlimmsten Fall führen Sie den Code aus, um ein oder zwei zusätzliche Zeiten zu überprüfen.
Dan Rosenstark

Ich meine, wenn ein Prozess versucht, bereits zu überprüfen, während ein anderer gerade diesen Wert liest, kann die App abstürzen. Ich würde diese Zeile hinzufügen: @synchronyze (bereits überprüft) {bereits überprüft = JA}
Nikolay Shubenkov

2

Schnelle Version der obigen Antworten mit einer Skala von> = 2,0, sodass das iPhone 6+ und andere zukünftige Geräte mit einer Skala über der Retina enthalten sind:

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

1

Um die Antwort von @sickp und den folgenden Kommentar von @ n13 zu kombinieren, habe ich dies zu einer UIScreen-Kategorie gemacht, die anscheinend gut funktioniert. Die Prüfung wird beim ersten Aufruf durchgeführt und dann für spätere Anrufe gespeichert.

@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

Könnte für jemanden nützlich sein.


Danke für den Caching-Code. Mein einziger Vorschlag ist, dies zu machen, (Util)anstatt (RetinaCheck)... vielleicht ist es weniger klar, aber es eignet sich für andere Zwecke. Ich würde auch die Methode isRetinaDisplayoder etwas nennen, das damit beginnt is, aber vielleicht habe ich die Richtlinien für Obj-C nie verstanden. Ich bin auch ein Fan von, > 1.0aber wer weiß, was in Zukunft Sinn macht.
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;
}

Beste Lösung, wie ich denke.
Nikolay Shubenkov

0

Versuche dies

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

Modifizierte Version von Primulaveris zur Vereinfachung der häufigsten Anwendungsfälle. Ich bin auf Swift 2.2, aber es sollte keine Rolle spielen.

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

Dann benutze sie einfach so

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

0

Das hat bei mir funktioniert

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.