Deteksi Tampilan Retina


223

Apakah iOS SDK menyediakan cara mudah untuk memeriksa apakah currentDevice memiliki tampilan (retina) resolusi tinggi?

Cara terbaik yang saya temukan untuk melakukannya sekarang adalah:

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

Karena penasaran - apa yang Anda lakukan ketika Anda mendeteksi tampilan selain menunjukkan versi yang lebih besar dari karya seni Anda?
Michael Behan


@mbehan: Saya memiliki TTImageView (lihat kerangka Three20) dan saya ingin memberikan url resolusi tinggi dari gambar.
Pierre Valade

1
Pertanyaan ini juga berguna bagi saya karena saya telah mengunduh gambar yang hadir sebagai UI tersedia dalam ukuran untuk semua 4 ukuran tampilan & hanya ingin agar pengguna mengunduh yang sesuai.
Pedro

@mbehan: dalam kasus saya, saya ingin pemisah sel khusus yang 1px di kedua layar retina & non-retina (seperti pemisah asli). Mengatur ketebalan hingga 1px menjadikan pada 2px pada tampilan retina (jelas).
user3099609

Jawaban:


295

Untuk mendeteksi tampilan Retina dengan andal di semua perangkat iOS, Anda perlu memeriksa apakah perangkat itu menjalankan iOS4 + dan apakah [UIScreen mainScreen].scalepropertinya sama dengan 2.0. Anda TIDAK BISA mengasumsikan perangkat menjalankan iOS4 + jika scaleproperti ada, karena iPad 3.2 juga mengandung properti ini.

Pada iPad yang menjalankan iOS3.2, skala akan mengembalikan 1.0 dalam mode 1x, dan 2.0 dalam mode 2x - meskipun kita tahu bahwa perangkat tidak mengandung layar Retina. Apple mengubah perilaku ini di iOS4.2 untuk iPad: ini mengembalikan 1.0 dalam mode 1x dan 2x. Anda dapat menguji ini sendiri di simulator.

Saya menguji -displayLinkWithTarget:selector:metode pada layar utama yang ada di iOS4.x tetapi tidak iOS3.2, dan kemudian memeriksa skala layar:

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

Anda mengatakan bahwa "Apple mengubah perilaku ini di iOS4.2 untuk iPad", menyiratkan bahwa di iOS4.1, kode Anda di atas akan kembali "adalah Retina" untuk iPad yang menjalankan aplikasi iPhone dalam mode 2x. Apakah aku salah?
makdad

9
Tidak pernah ada 4.1 untuk iPad. Hanya 3,2, lalu 4,2.
Jonny

11
Panggilan ini agak mahal jadi saya akan menginisialisasi BOOL dengan itu di awal aplikasi dan menggunakannya di aplikasi.
n13

Saya lebih suka memeriksa versi menggunakan [UIDevice currentDevice].systemVersion]. Dalam hal ini NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman

Tampaknya tidak berfungsi di simulator untuk iPad non retina (ios 7.1) di xcode 4 ... aneh.
Isaac Paul

81

@ jawaban sickp benar. Untuk mempermudah, tambahkan baris ini ke file Shared.pch Anda:

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

Kemudian dalam file apa saja Anda bisa melakukan:

if(IS_RETINA)
{
   // etc..
}

Ini tidak berfungsi pada simulator. Apakah ini karena resposToSelektor? Simulator tidak merespons pemilih?
arniotaki

2
Bagus! Namun jika Anda ingin memperhitungkan iPhone 6 Plus, Anda harus memeriksa skala> = 2.0.
Ivan Carosati

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

23
Mengapa ?1:0? Bukankah itu hanya mengulangi apa yang sudah dihitung pada bagian boolean dari ekspresi?
d11wtq

9

Berikut ini adalah ekstensi cepat yang berguna:

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

Pemakaian:

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

Asli:

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

Pemakaian:

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

Bukankah seharusnya kode yang diperbarui berfungsi untuk Swift 5 isRetinaHD periksa apakah iscreenScale>> 3.0 bukan 2.0 ??? Sunting: Saya memperbaruinya ...
C0D3

6

Cuplikan ini ...

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

Akan kembali ... 0 untuk iPhone resolusi standar / iPod touch, 1 untuk iPhone retina, 2 untuk iPad resolusi standar, 3 untuk iPad retina.



5

Selalu terasa agak cerdik untuk membandingkan nilai floating-point untuk kesetaraan. Saya lebih suka pergi untuk keduanya

[UIScreen mainScreen].scale > 1.0;

atau

[UIScreen mainScreen].scale < 2.0;

5
Membandingkan dua nilai floating-point untuk kesetaraan "terasa cerdik", karena mereka dapat sedikit berbeda dari nilai integral setelah perhitungan. Tetapi membandingkan dengan <atau> sama cerdiknya dengan situasi itu. Dalam kasus ini, bagaimanapun, tidak ada peluang sama sekali bahwa skala tidak tepat 1,0 atau 2,0, karena didefinisikan oleh perangkat keras.
ikan dekat

Seperti yang disarankan @fishinear, lebih baik menggunakan sesuatu seperti isRetina = [UIScreen mainScreen].scale > 1.95. Ini juga akan bermanfaat untuk ketahanan ketika @ 4x datang :)
Danyal Aytekin

Saya sangat tidak setuju. Melakukan hal ini ketika tidak diperlukan membuat kode lebih mudah dibaca. Poin di masa depan-bukti mungkin memiliki validitasnya, tapi saya ragu kita akan memiliki layar @ 4x dalam waktu dekat (jika sama sekali).
Ricardo Sanchez-Saez

Salah. hanya karena itu adalah "perangkat keras yang ditentukan" tidak berarti Anda menghindari masalah compare-a-float. (Ini hanya float seperti yang lain.) Seperti float apa pun, secara umum Anda tidak pernah dapat menggunakan ==, Anda harus menggunakan perbandingan> atau <. Bagaimana dengan> 1,5 untuk kepastian.
Fattie

2

Ini adalah riff pada jawaban Matt MC di atas. Hanya sebuah kategori 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
Saya curiga bahwa caching alreadyCheckeditu gratis, tapi tidak apa-apa.
Dan Rosenstark

@NikolayShubenkov itu sebabnya saya atur sudah memeriksa terakhir. Dalam skenario terburuk, Anda menjalankan kode untuk memeriksa satu atau dua waktu tambahan.
Dan Rosenstark

Maksud saya ketika satu proses akan mencoba sudah diperiksa sementara yang lain sedang membaca aplikasi nilai ini mungkin macet. Saya akan menambahkan baris itu: @synchronyze (sudahChecked) {sudahChecked = YA}
Nikolay Shubenkov

2

Versi cepat jawaban di atas, dengan skala> = 2.0 sehingga mencakup iPhone 6+ dan perangkat masa depan lainnya dengan skala lebih tinggi dari retina:

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

1

Hanya untuk menggabungkan jawaban dari @sickp dan komentar berikut dari @ n13, saya menjadikan ini sebagai kategori UIScreen yang tampaknya berfungsi dengan baik. Pemeriksaan dilakukan pertama kali Anda memanggilnya dan kemudian disimpan untuk panggilan selanjutnya.

@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

Mungkin bermanfaat bagi seseorang.


Terima kasih untuk kode caching. Satu-satunya saran saya adalah membuat ini (Util)alih-alih (RetinaCheck)... mungkin kurang jelas, tetapi cocok untuk penggunaan lain. Juga saya akan menyebutkan metode isRetinaDisplayatau sesuatu yang dimulai dengan is, tetapi mungkin saya tidak pernah mengerti pedoman untuk Obj-C. Juga, saya penggemar > 1.0tetapi siapa yang tahu apa yang masuk akal bergerak maju.
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;
}

Solusi terbaik seperti yang saya pikirkan.
Nikolay Shubenkov

0

coba ini

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

Versi primulaveris yang dimodifikasi untuk kesederhanaan kasus penggunaan yang paling umum. Saya pada 2.2 cepat tapi itu tidak masalah.

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

Maka cukup gunakan mereka seperti ini

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

0

Ini berhasil untuk saya

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.