Wykryj wyświetlacz Retina

223

Czy zestaw iOS SDK zapewnia łatwy sposób sprawdzenia, czy bieżące urządzenie ma wyświetlacz o wysokiej rozdzielczości (siatkówka)?

Najlepszym sposobem, w jaki znalazłem to teraz, jest:

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES && [[UIScreen mainScreen] scale] == 2.00) {
         // RETINA DISPLAY
    }
Pierre Valade
źródło
Z ciekawości - co robisz, gdy wykrywasz inny ekran niż pokazywanie większych wersji swojego dzieła?
Michael Behan,
4
możliwy duplikat Jak odróżnić iPhone 4 od iPhone 3
Kendall Helmstetter Gelner
@mbehan: Mam TTImageView (patrz framework Three20) i chcę podać adres URL obrazu w wysokiej rozdzielczości.
Pierre Valade,
1
To pytanie jest również dla mnie przydatne, ponieważ pobrałem obrazy prezentowane jako interfejs użytkownika dostępne w rozmiarach dla wszystkich 4 rozmiarów ekranu i chcę tylko, aby użytkownicy pobrali odpowiedni.
Pedro
@mbehan: w moim przypadku chciałem niestandardowych separatorów komórek o wielkości 1 piksela na ekranach zarówno siatkówki, jak i innych niż siatkówka (takich jak separatory natywne). Ustawienie grubości na 1px renderuje przy 2px na wyświetlaczach siatkówki (oczywiście).
user3099609,

Odpowiedzi:

295

Aby niezawodnie wykryć wyświetlacz Retina na wszystkich urządzeniach z iOS, musisz sprawdzić, czy na urządzeniu działa iOS4 + i czy [UIScreen mainScreen].scalewłaściwość jest równa 2,0. NIE MOŻNA założyć, że na urządzeniu działa system iOS4 +, jeśli scalewłaściwość istnieje, ponieważ iPad 3.2 również zawiera tę właściwość.

Na iPadzie z iOS3.2 waga zwróci 1.0 w trybie 1x, a 2.0 w trybie 2x - mimo że wiemy, że urządzenie nie zawiera wyświetlacza Retina. Apple zmieniło to zachowanie w iOS 4.2 dla iPada: zwraca 1,0 w trybie 1x i 2x. Możesz to przetestować samodzielnie w symulatorze.

I test dla -displayLinkWithTarget:selector:metody na ekranie głównym, który istnieje w iOS4.x ale nie iOS3.2, a następnie sprawdzić skalę przez ekran:

if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] &&
    ([UIScreen mainScreen].scale == 2.0)) {
  // Retina display
} else {
  // non-Retina display
}
chory
źródło
Mówisz, że „Apple zmieniło to zachowanie w iOS 4.2 na iPada”, co oznacza, że ​​w iOS 4.1 powyższy kod zwróciłby „to Retina” na iPadzie z uruchomioną aplikacją na iPhone w trybie 2x. Czy się mylę?
makdad
9
Nigdy nie było 4.1 dla iPada. Tylko 3,2, a następnie 4,2.
Jonny
11
To połączenie jest nieco drogie, więc zainicjowałbym BOOL przy uruchamianiu aplikacji i używałbym go w aplikacji.
n13
Wolę sprawdzić wersję za pomocą [UIDevice currentDevice].systemVersion]. W tym przypadku byłby to NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman,
Wydaje się, że nie działa w symulatorze dla iPada bez siatkówki (ios 7.1) w Xcode 4 ... dziwne.
Izaak Paweł
81

Odpowiedź @ sickp jest poprawna. Aby to ułatwić, dodaj tę linię do pliku Shared.pch:

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

Następnie w dowolnym pliku możesz po prostu zrobić:

if(IS_RETINA)
{
   // etc..
}
Mick Byrne
źródło
To nie działa na symulatorze. Czy to z powodu respondsToSelector? Symulator nie reaguje na selektor?
arniotaki,
2
Wspaniały! Jeśli jednak chcesz wziąć pod uwagę iPhone'a 6 Plus, powinieneś sprawdzić skalę> = 2.0.
Ivan Carosati
20
+(BOOL)iPhoneRetina{
    return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?1:0;
}
Mani
źródło
23
Dlaczego ?1:0? Czy to nie tylko powtarzanie tego, co zostało już obliczone w części logicznej wyrażenia?
d11wtq
9

Oto przydatne szybkie rozszerzenie:

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

Stosowanie:

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

Oryginalny:

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

Stosowanie:

if UIScreen.mainScreen().isRetina() {
 // your code
        }
primulaveris
źródło
Czy kod, który aktualizuje się do działania w Swift 5, to RetinaHD, czy iscreenScale to> = 3.0, a nie 2.0? Edycja: zaktualizowałem go ...
C0D3
6

Ten fragment kodu ...

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

Wróci ... 0 dla standardowej rozdzielczości iPhone / iPod touch, 1 dla Retina iPhone, 2 dla standardowej rozdzielczości iPad, 3 dla Retina iPad.

Pedro
źródło
5

Porównywanie wartości zmiennoprzecinkowych dla równości zawsze wydaje się nieco podejrzane. Wolę wybrać jedno lub drugie

[UIScreen mainScreen].scale > 1.0;

lub

[UIScreen mainScreen].scale < 2.0;
skahlert
źródło
5
Porównywanie dwóch wartości zmiennoprzecinkowych dla równości „wydaje się niejasne”, ponieważ mogą się nieznacznie różnić od wartości całkowitych po obliczeniach. Ale porównywanie z <lub> jest równie niepewne w takich sytuacjach. W tym przypadku jednak nie ma żadnej szansy, że skala nie jest dokładnie 1,0 lub 2,0, ponieważ jest zdefiniowana sprzętowo.
fishinear
Jak sugeruje @fishinear, lepiej użyć czegoś takiego isRetina = [UIScreen mainScreen].scale > 1.95. Będzie to miało również tę zaletę, że będzie odporny na pojawienie się @ 4x :)
Danyal Aytekin
Zdecydowanie się nie zgadzam. Wykonanie tego, gdy nie jest potrzebne, powoduje, że kod jest mniej czytelny. Punkt na przyszłość może mieć swoją ważność, ale wątpię, abyśmy mieli wkrótce ekrany 4x (jeśli w ogóle).
Ricardo Sanchez-Saez
Źle. sam fakt, że jest on „zdefiniowany sprzętowo”, w żaden sposób nie oznacza, że ​​unikasz problemu porównywania zmiennoprzecinkowego. (Jest to po prostu zmiennoprzecinkowy jak każdy inny.) Podobnie jak w przypadku każdego zmiennoprzecinkowego, ogólnie rzecz biorąc, nigdy nie można używać ==, należy użyć porównania> lub <. Co z pewnością> 1,5.
Fattie
2

To riff na powyższej odpowiedzi Matta MC. Tylko kategoria na 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
Dan Rosenstark
źródło
1
Podejrzewam, że buforowanie alreadyCheckedjest bezpłatne, ale jest w porządku.
Dan Rosenstark,
@NikolayShubenkov dlatego ustawiłem już sprawdzone jako ostatnie. W najgorszym przypadku kod jest uruchamiany w celu sprawdzenia dodatkowego czasu lub dwóch.
Dan Rosenstark,
Mam na myśli, gdy jeden proces spróbuje już sprawdzić, podczas gdy inny obecnie czyta tę wartość, aplikacja może ulec awarii. Dodałbym tę linię: @synchronyze (jużSprawdzone) {jużSprawdzone = TAK}
Nikolay Shubenkov
2

Szybka wersja powyższych odpowiedzi, ze skalą> = 2.0, więc obejmuje iPhone'a 6+ i inne przyszłe urządzenia ze skalą wyższą niż Retina:

 if UIScreen.mainScreen().respondsToSelector(Selector("scale")) && UIScreen.mainScreen().scale >= 2.0 {
    // code executed only on Retina device
}
cdf1982
źródło
1

Aby połączyć odpowiedź z @sickp i następujący komentarz z @ n13, przekształciłem to w kategorię UIScreen, która wydaje się ładnie działać. Sprawdzanie jest wykonywane przy pierwszym połączeniu, a następnie zapisywane do późniejszych połączeń.

@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

Może być komuś przydatny.

Matt Mc
źródło
Dzięki za kod pamięci podręcznej. Moją jedyną propozycją jest uczynienie tego (Util)zamiast (RetinaCheck)... być może mniej zrozumiałym, ale nadaje się do innych zastosowań. Nazwałbym też metodę isRetinaDisplaylub coś, co zaczyna się od is, ale może nigdy nie zrozumiałem wytycznych dla Obj-C. Poza tym jestem fanem, > 1.0ale kto wie, co będzie sensowne, iść naprzód.
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;
}
Roman Solodyashkin
źródło
Najlepsze rozwiązanie, jak myślę.
Nikolay Shubenkov
0

Spróbuj tego

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");
}
KARTHIK RA
źródło
0

Zmodyfikowana wersja Primulaveris dla uproszczenia najczęściej spotykanych przypadków użycia. Jestem na szybkim 2.2, ale to nie powinno mieć znaczenia.

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

Następnie po prostu użyj ich w ten sposób

print(UIScreen.isRetina)
print(UIScreen.isRetinaHD)
print(UIScreen.screenScale)
GregP
źródło
0

To zadziałało dla mnie

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Michael Fretz
źródło