Обнаружение дисплея Retina

Предоставляет ли iOS SDK простой способ проверить, есть ли на текущем устройстве дисплей с высоким разрешением (сетчатка)?

Лучший способ, который я нашел сейчас, - это:

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

person Pierre Valade    schedule 17.08.2010    source источник
comment
Из любопытства - что вы делаете, когда обнаруживаете дисплей, кроме демонстрации более крупных версий вашего произведения искусства?   -  person Michael Behan    schedule 17.08.2010
comment
возможный дубликат Как отличить iphone4 от iphone 3   -  person Kendall Helmstetter Gelner    schedule 17.08.2010
comment
@mbehan: у меня есть TTImageView (см. фреймворк Three20), и я хочу предоставить URL-адрес изображения с высоким разрешением.   -  person Pierre Valade    schedule 18.08.2010
comment
Этот вопрос также полезен для меня, потому что я загрузил изображения, которые представлены в виде пользовательского интерфейса, доступных в размерах для всех 4 размеров дисплеев, и я хочу, чтобы пользователи загружали только соответствующий.   -  person Pedro    schedule 22.08.2012
comment
@mbehan: в моем случае мне нужны настраиваемые разделители ячеек размером 1 пиксель как на сетчатке, так и на экранах без сетчатки (например, собственные разделители). Установка толщины на 1 пиксель отрисовывает 2 пикселя на дисплеях сетчатки (очевидно).   -  person user3099609    schedule 19.09.2014
comment
Не забывайте, что с iPhone6 ​​и 6 plus вам нужно тестировать масштаб «› = 2.0 », а не только« == 2.0 ».   -  person James    schedule 17.12.2014


Ответы (14)


Чтобы надежно обнаружить дисплей 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
}
person sickp    schedule 09.01.2011
comment
Вы говорите, что Apple изменила это поведение в iOS4.2 для iPad, подразумевая, что в iOS4.1 приведенный выше код вернет Retina для iPad, на котором запущено приложение iPhone в режиме 2x. Я ошибся? - person makdad; 11.03.2012
comment
Для iPad версии 4.1 никогда не было. Только 3,2, потом 4,2. - person Jonny; 02.04.2012
comment
Этот вызов немного дороже, поэтому я бы инициализировал с ним BOOL при запуске приложения и использовал его в приложении. - person n13; 20.05.2012
comment
Я предпочитаю проверять версию с помощью [UIDevice currentDevice].systemVersion]. В этом случае это будет NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch]; - person Sandy Chapman; 09.12.2013
comment
Кажется, не работает в симуляторе для iPad без сетчатки (ios 7.1) в xcode 4 ... странно. - person Isaac Paul; 04.08.2014
comment
Почему нужно проверять @selector(displayLinkWithTarget:selector:), а не @selector(scale)? - person Alexander Farber; 20.04.2015

Ответ @ickp правильный. Чтобы упростить задачу, добавьте эту строку в свой файл Shared.pch:

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

Затем в любом файле вы можете просто сделать:

if(IS_RETINA)
{
   // etc..
}
person Mick Byrne    schedule 21.02.2013
comment
Это не работает на симуляторе. Это из-за RespondsToSelector? Симулятор не реагирует на селектор? - person arniotaki; 19.02.2015
comment
Здорово! Однако, если вы хотите принять во внимание iPhone 6 Plus, вам следует проверить масштаб ›= 2.0. - person Ivan Carosati; 15.04.2015

Вот удобное быстрое расширение:

Обновление для 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
        }
person primulaveris    schedule 15.01.2016
comment
Разве обновленный код для Swift 5 isRetinaHD не должен проверять, имеет ли iscreenScale значение ›= 3.0, а не 2.0 ??? Изменить: я обновил его ... - person C0D3; 12.03.2020

Этот фрагмент ...

    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 с сетчаткой.

person Pedro    schedule 22.08.2012


Всегда кажется немного изворотливым сравнивать значения с плавающей запятой на предмет равенства. Я предпочитаю либо

[UIScreen mainScreen].scale > 1.0;

or

[UIScreen mainScreen].scale < 2.0;
person skahlert    schedule 07.03.2013
comment
Сравнение двух значений с плавающей запятой на предмет равенства кажется изворотливым, потому что после вычислений они могут немного отличаться от целых значений. Но сравнивать с ‹или› в таких ситуациях столь же хитроумно. В этом случае, однако, нет никаких шансов, что масштаб не совсем 1.0 или 2.0, поскольку он определяется аппаратно. - person fishinear; 22.03.2013
comment
Как предлагает @fishinear, лучше использовать что-то вроде isRetina = [UIScreen mainScreen].scale > 1.95. Это также будет устойчивым к появлению @ 4x :) - person Danyal Aytekin; 06.08.2013
comment
Я категорически не согласен. Это делает код менее читаемым. Точка зрения о защите от будущего может иметь свое значение, но я сомневаюсь, что в ближайшее время у нас появятся экраны @ 4x (если они вообще появятся). - person Ricardo Sanchez-Saez; 16.08.2013
comment
Неправильный. Тот факт, что он определен аппаратно, никоим образом не означает, что вы избегаете проблемы сравнения с плавающей точкой. (Это просто число с плавающей запятой, как и любое другое.) Как и в случае с любым другим числом с плавающей запятой, в общем случае вы никогда не можете использовать ==, вы должны использовать сравнение ›или‹. Насчет ›1.5 наверняка. - person Fattie; 30.03.2014

Это рифф на ответ Мэтта МС выше. Просто категория на 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
person Dan Rosenstark    schedule 13.11.2013
comment
Я подозреваю, что кеширование alreadyChecked неоправданно, но это нормально. - person Dan Rosenstark; 29.11.2013
comment
@NikolayShubenkov именно поэтому я поставил ужеChecked последним. В худшем случае вы запускаете код для проверки еще раз или два. - person Dan Rosenstark; 23.04.2014
comment
Я имею в виду, что когда один процесс попытается уже проверить, в то время как другой в настоящее время читает это значение, приложение может аварийно завершить работу. Я бы добавил эту строку: @synchronyze (ужеChecked) {alreadyChecked = YES} - person Nikolay Shubenkov; 25.04.2014

Быстрая версия ответов выше со шкалой> = 2.0, поэтому она включает iPhone 6+ и другие будущие устройства со шкалой выше Retina:

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

Чтобы объединить ответ от @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

Может быть кому-то полезно.

person Matt Mc    schedule 04.07.2013
comment
Спасибо за код кеширования. Единственное, что я предлагаю - сделать это (Util) вместо _2 _..., возможно, он менее ясен, но он подходит для других целей. Также я бы назвал метод isRetinaDisplay или что-то, что начинается с is, но, возможно, я никогда не понимал руководящих принципов для Obj-C. Кроме того, я фанат > 1.0, но кто знает, что будет иметь смысл в дальнейшем. - person Dan Rosenstark; 14.11.2013

попробуй это

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");
}
person KARTHIK RA    schedule 08.01.2014

Модифицированная версия primulaveris's для простоты наиболее распространенных вариантов использования. Я использую swift 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)
person GregP    schedule 12.04.2016

Это сработало для меня

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
person Michael Fretz    schedule 12.05.2016

person    schedule
comment
Почему ?1:0? Разве это не повторение того, что уже было вычислено в логической части выражения? - person d11wtq; 04.02.2013

person    schedule
comment
Как мне кажется, лучшее решение. - person Nikolay Shubenkov; 22.04.2014