Виявити дисплей сітківки


223

Чи надає iOS SDK простий спосіб перевірити, чи поточний пристрій має дисплей високої роздільної здатності (сітківку)?

Найкращий спосіб, який я знайшов це зробити зараз:

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

З цікавості - що ви робите, коли виявляєте дисплей, окрім показу великих версій свого мистецького твору?
Майкл Бехан


@mbehan: У мене є TTImageView (див. рамку Three20), і я хочу надати URL-адресу зображення з високою роздільною здатністю.
П’єр Валаде

1
Це питання також корисне для мене, оскільки я завантажив зображення, які представлені як інтерфейс користувача, доступних у розмірах для всіх 4 розмірів дисплея і хочу лише, щоб користувачі завантажили відповідне.
Педро

@mbehan: у моєму випадку я хотів користувацьких роздільників комірок, які на 1 px як на сітківці, так і на неретинальних екранах (як на рідних сепараторах). Встановлення товщини в 1 пікс відображається на 2 пікселів на сітківках (очевидно).
user3099609

Відповіді:


295

Щоб надійно виявити дисплей Retina на всіх пристроях iOS, потрібно перевірити, чи пристрій працює під iOS4 + та чи [UIScreen mainScreen].scaleвластивість дорівнює 2,0. Ви НЕ МОЖЕТЕ припустити, що пристрій працює під iOS4 +, якщо scaleвластивість існує, оскільки iPad 3.2 також містить цю властивість.

На iPad, який працює під управлінням iOS3.2, масштаб повертає 1,0 в режимі 1x, а 2,0 в режимі 2x - хоча ми знаємо, що пристрій не містить дисплея Retina. Apple змінила цю поведінку в iOS4.2 для iPad: вона повертає 1,0 в обох режимах 1x та 2x. Ви можете перевірити це самостійно в тренажері.

Я перевіряю -displayLinkWithTarget:selector:метод на головному екрані, який існує в iOS4.x, але не iOS3.2, а потім перевіряю масштаб екрану:

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

Ви говорите, що "Apple змінила таку поведінку в iOS4.2 для iPad", маючи на увазі, що в iOS4.1 ваш код вище повернеться "Retina" для iPad, що працює в додатку iPhone в 2x режимі. Я помиляюся?
макдад

9
Ніколи не було 4.1 для iPad. Тільки 3,2, потім 4,2.
Джоні

11
Цей дзвінок коштує трохи дорого, тому я би ініціалізував BOOL з ним при запуску програми і використовував його в додатку.
n13,

Я вважаю за краще перевірити версію за допомогою [UIDevice currentDevice].systemVersion]. У цьому випадку це було б NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Сенді Чапман

Здається, не працює в тренажері для iPad без сітківки (ios 7.1) в xcode 4 ... дивно.
Ісаак Пол

81

@ відповідь хвороба правильна. Щоб полегшити ситуацію, додайте цей рядок у файл Shared.pch:

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

Тоді в будь-якому файлі ви можете просто зробити:

if(IS_RETINA)
{
   // etc..
}

Це не працює на тренажері. Це через відповідьToSelector? Симулятор не відповідає селектору?
арніотакі

2
Чудово! Однак якщо ви хочете взяти до уваги iPhone 6 Plus, вам слід перевірити масштаб> = 2.0.
Іван Каросаті

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

23
Чому ?1:0? Хіба це не лише повторення того, що вже було обчислено в бульній частині виразу?
d11wtq

9

Ось зручне швидке розширення:

Оновлення для 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
    }
}

Використання:

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

Оригінал:

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

Використання:

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

Чи не повинен код, оновлений для роботи для Swift 5, перевірити, чи є iscreenScale> = 3.0, а не 2.0 ??? Редагувати: я оновив його ...
C0D3

6

Цей фрагмент ...

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

Повернеться ... 0 для iPhone / iPod touch зі стандартною роздільною здатністю, 1 для iPhone із сітківкою, 2 для iPad зі стандартною роздільною здатністю, 3 для iPad сітківки.



5

Завжди відчуваєш трохи хитрості порівняння значень з плаваючою комою для рівності. Я вважаю за краще йти на будь-який

[UIScreen mainScreen].scale > 1.0;

або

[UIScreen mainScreen].scale < 2.0;

5
Порівняння двох значень з плаваючою комою для рівності "відчуває хиткість", оскільки вони можуть трохи відрізнятися від інтегральних значень після обчислень. Але порівняння з <або> є настільки ж хитрим у тих ситуаціях. У цьому випадку, однак, немає жодного шансу, що масштаб не буде точно 1,0 або 2,0, як це визначено апаратно.
риболовля

Як пропонує @fishinear, краще використовувати щось подібне isRetina = [UIScreen mainScreen].scale > 1.95. Це також матиме перевагу бути стійкими до того, коли @ 4x з'явиться :)
Danyal Aytekin

Я категорично не згоден. Якщо це не потрібно, код робить менш читабельним. Точка щодо стійкості до майбутнього може мати свою справедливість, але я сумніваюся, що незабаром у нас з’явиться @ 4x екрани (якщо вони є).
Рікардо Санчес-Саес

Неправильно. лише тому, що це "апаратно визначено", в жодному разі не означає, що ви уникаєте проблеми порівняння-поплавка. (Це просто поплавок, як і будь-який інший.) Як і будь-який поплавок, загалом ви ніколи не можете використовувати ==, ви повинні використовувати порівняння> або <. Як щодо> 1,5 для впевненості.
Fattie

2

Це риф на відповідь Метта МС вище. Просто категорія на 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
Я підозрюю, що кешування alreadyCheckedбезкоштовно, але це добре.
Дан Розенстарк

@NikolayShubenkov, тому я поставив вже перевірений останнім. У гіршому випадку ви запускаєте код, щоб перевірити додатковий час або два.
Dan Rosenstark

Я маю на увазі, коли один процес спробує вже перевірити, а інший зараз читає це значення, додаток може вийти з ладу. Я додав би цей рядок: @synchronyze (вже перевірено) {вжеChecked = ТАК}
Микола Шубенков

2

Швидка версія відповідей вище, зі шкалою> = 2,0, тому вона включає iPhone 6+ та інші майбутні пристрої з більшою шкалою, ніж ретина:

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

1

Просто поєднуючи відповідь від @sickp та наступний коментар від @ n13, я зробив це у категорії UIScreen, яка, здається, працює добре. Перевірка проводиться в перший раз, коли ви її телефонуєте, а потім зберігаєте для наступних дзвінків.

@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

Можливо, комусь буде корисно.


Дякуємо за код кешування Моя єдина пропозиція - зробити це (Util)замість (RetinaCheck)... можливо, це менш зрозуміло, але воно піддається іншим способам використання. Також я б назвав метод isRetinaDisplayабо щось, з чого починається is, але, можливо, я ніколи не розумів вказівки щодо Obj-C. Також я фанат, > 1.0але хто знає, що буде сенс рухатися вперед.
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;
}

Як я думаю, найкраще рішення.
Микола Шубенков

0

спробуйте це

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

Модифікована версія primulaveris для простоти найбільш поширених випадків використання. Я на швидкому 2.2, але це не має значення.

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

Тоді просто використовуйте їх так

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.