ตรวจสอบจอแสดงผล Retina


223

iOS SDK มีวิธีง่าย ๆ ในการตรวจสอบว่าอุปกรณ์ปัจจุบันมีจอแสดงผลความละเอียดสูง (เรตินา) หรือไม่?

วิธีที่ดีที่สุดที่ฉันพบตอนนี้คือ:

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

ด้วยความอยากรู้ - คุณกำลังทำอะไรเมื่อคุณตรวจจับจอแสดงผลนอกเหนือจากการแสดงงานศิลปะรุ่นใหญ่ของคุณ?
Michael Behan


@mbehan: ฉันมี TTImageView (ดูกรอบที่สาม) และฉันต้องการให้ URL ของภาพที่มีความละเอียดสูง
Pierre Valade

1
คำถามนี้มีประโยชน์กับฉันเช่นกันเพราะฉันได้ดาวน์โหลดรูปภาพที่แสดงเป็น UI ที่มีอยู่ในขนาดสำหรับขนาดจอแสดงผลทั้ง 4 และต้องการให้ผู้ใช้ดาวน์โหลดที่เหมาะสมเท่านั้น
Pedro

@mbehan: ในกรณีของฉันฉันต้องการตัวคั่นเซลล์แบบกำหนดเองที่ 1px บนทั้งหน้าจอเรตินาและไม่ใช่เรตินา (เช่นตัวคั่นเนทิฟ) การตั้งค่าความหนาเป็น 1px แสดงผลที่ 2px บนหน้าจอเรตินา (ชัด)
3099609

คำตอบ:


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 ฉันผิดหรือเปล่า?
makdad

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];
Sandy Chapman

ดูเหมือนจะไม่ทำงานในเครื่องจำลองสำหรับ iPad ที่ไม่ใช่เรตินา (iOS 7.1) ใน xcode 4 ... แปลก
Isaac Paul

81

คำตอบของ @ sickp ถูกต้อง เพื่อให้ง่ายขึ้นเพิ่มบรรทัดนี้ลงในไฟล์ Shared.pch ของคุณ:

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

จากนั้นในไฟล์ใด ๆ ที่คุณสามารถทำได้:

if(IS_RETINA)
{
   // etc..
}

สิ่งนี้ไม่ทำงานในโปรแกรมจำลอง มันเป็นเพราะการตอบสนองต่อตัวเลือก? Simulator ไม่ตอบสนองต่อการเลือก?
arniotaki

2
ที่ดี! อย่างไรก็ตามหากคุณต้องการพิจารณา iPhone 6 Plus คุณควรตรวจสอบมาตราส่วน> = 2.0
Ivan Carosati

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 isRetinaHD ตรวจสอบว่า 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 หน้าจอได้ตลอดเวลาเร็ว ๆ นี้ (ถ้าทั้งหมด)
Ricardo Sanchez-Saez

ไม่ถูกต้อง. เพียงเพราะมันเป็น "ฮาร์ดแวร์ที่กำหนด" ไม่ได้หมายความว่าคุณหลีกเลี่ยงปัญหา (มันเป็นเพียงโฟลทที่เหมือนกัน) เช่นเดียวกับโฟลตใด ๆ โดยทั่วไปคุณไม่สามารถใช้ == ได้คุณต้องใช้> หรือ <การเปรียบเทียบ > 1.5 แน่นอน
Fattie

2

นี่เป็นคำตอบของ Matt MC ด้านบน 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ฟรี แต่มันก็ดี
Dan Rosenstark

@NikolayShubenkov นั่นเป็นเหตุผลที่ฉันตั้งค่าแล้วตรวจสอบล่าสุด ในสถานการณ์กรณีที่เลวร้ายที่สุดคุณเรียกใช้รหัสเพื่อตรวจสอบเวลาพิเศษหรือสอง
Dan Rosenstark

ฉันหมายถึงเมื่อกระบวนการหนึ่งจะพยายามตรวจสอบแล้วในขณะที่กระบวนการอื่นกำลังอ่านแอปค่านี้อาจมีปัญหา ฉันจะเพิ่มบรรทัดนั้น: @synchronyze (onlyChecked) {thenChecked = YES}
Nikolay Shubenkov

2

คำตอบเวอร์ชันที่รวดเร็วด้วย> = 2.0 สเกลดังนั้นจึงรวม iPhone 6+ และอุปกรณ์อื่น ๆ ในอนาคตที่มีสเกลเรตมากกว่า Retina:

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

ทางออกที่ดีที่สุดอย่างที่ฉันคิด
Nikolay Shubenkov

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)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.