Phát hiện màn hình Retina


223

IOS SDK có cung cấp một cách dễ dàng để kiểm tra xem currentDevice có màn hình độ phân giải cao (võng mạc) không?

Cách tốt nhất mà tôi tìm thấy để làm điều đó bây giờ là:

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

Vì tò mò - bạn đang làm gì khi phát hiện màn hình ngoài việc hiển thị các phiên bản lớn hơn của tác phẩm nghệ thuật của bạn?
Michael Behan


@mbehan: Tôi có một TTImageView (xem khung Three20) và tôi muốn cung cấp một url có độ phân giải cao của hình ảnh.
Pierre Valade

1
Câu hỏi này cũng hữu ích với tôi vì tôi đã tải xuống hình ảnh có giao diện người dùng có sẵn ở các kích cỡ cho cả 4 kích cỡ hiển thị & chỉ muốn người dùng tải xuống hình ảnh phù hợp.
Pedro

@mbehan: trong trường hợp của tôi, tôi muốn các dấu tách ô tùy chỉnh có kích thước 1px trên cả màn hình võng mạc & không võng mạc (như dải phân cách riêng). Đặt độ dày thành 1px hiển thị ở mức 2px trên màn hình retina (rõ ràng).
dùng3099609

Câu trả lời:


295

Để phát hiện màn hình Retina đáng tin cậy trên tất cả các thiết bị iOS, bạn cần kiểm tra xem thiết bị có chạy iOS4 + hay không và thuộc [UIScreen mainScreen].scaletính có bằng 2.0 không. Bạn KHÔNG THỂ giả sử một thiết bị đang chạy iOS4 + nếu thuộc scaletính tồn tại, vì iPad 3.2 cũng chứa thuộc tính này.

Trên iPad chạy iOS3.2, tỷ lệ sẽ trả về 1.0 ở chế độ 1x và 2.0 ở chế độ 2x - mặc dù chúng tôi biết rằng thiết bị không chứa màn hình Retina. Apple đã thay đổi hành vi này trong iOS4.2 cho iPad: nó trả về 1.0 ở cả hai chế độ 1x và 2x. Bạn có thể tự kiểm tra điều này trong trình giả lập.

Tôi kiểm tra -displayLinkWithTarget:selector:phương thức trên màn hình chính tồn tại trong iOS4.x nhưng không phải iOS3.2, rồi kiểm tra tỷ lệ của màn hình:

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

Bạn nói rằng "Apple đã thay đổi hành vi này trong iOS4.2 cho iPad", ngụ ý rằng trong iOS4.1, mã của bạn ở trên sẽ trả về "là Retina" cho iPad chạy ứng dụng iPhone ở chế độ 2x. Liệu tôi có sai?
makdad

9
Chưa bao giờ có bản 4.1 cho iPad. Chỉ có 3,2, rồi 4.2.
Jonny

11
Cuộc gọi này hơi tốn kém vì vậy tôi sẽ khởi tạo BOOL với nó khi bắt đầu ứng dụng và sử dụng nó trong ứng dụng.
n13

Tôi thích kiểm tra phiên bản bằng cách sử dụng [UIDevice currentDevice].systemVersion]. Trong trường hợp này, đó là NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion]; return [currentSystemVersion compare:version options:NSNumericSearch];
Sandy Chapman

Dường như không hoạt động trong trình giả lập cho iPad không võng mạc (ios 7.1) trong xcode 4 ... kỳ lạ.
Isaac Paul

81

Câu trả lời của @ bệnh là đúng. Để làm cho mọi thứ dễ dàng hơn, hãy thêm dòng này vào tệp Shared.pch của bạn:

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

Sau đó, trong bất kỳ tập tin bạn chỉ có thể làm:

if(IS_RETINA)
{
   // etc..
}

Điều này không hoạt động trên giả lập. Có phải vì phản hồi ToSelector? Trình mô phỏng không đáp ứng với bộ chọn?
arniotaki

2
Tuyệt quá! Tuy nhiên, nếu bạn muốn tính đến iPhone 6 Plus, bạn nên kiểm tra tỷ lệ> = 2.0.
Ivan Carosati

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

23
Tại sao ?1:0? Không phải đó chỉ là nhắc lại những gì đã được tính toán trong phần boolean của biểu thức sao?
d11wtq

9

Đây là một phần mở rộng nhanh chóng tiện dụng:

Cập nhật cho 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
    }
}

Sử dụng:

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

Nguyên:

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

Sử dụng:

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

Không phải mã được cập nhật để hoạt động cho Swift 5 isRetinaHD kiểm tra nếu iscreenScale là> = 3.0 chứ không phải 2.0 ??? Chỉnh sửa: Tôi đã cập nhật nó ...
C0D3

6

Đoạn trích này ...

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

Sẽ trả về ... 0 cho iPhone có độ phân giải tiêu chuẩn, 1 cho iPhone retina, 2 cho iPad có độ phân giải tiêu chuẩn, 3 cho iPad retina.



5

Nó luôn cảm thấy một chút tinh ranh để so sánh các giá trị dấu phẩy động cho sự bình đẳng. Tôi thích đi cho một trong hai

[UIScreen mainScreen].scale > 1.0;

hoặc là

[UIScreen mainScreen].scale < 2.0;

5
So sánh hai giá trị dấu phẩy động cho đẳng thức "cảm thấy tinh ranh", bởi vì chúng có thể hơi khác với giá trị tích phân sau khi tính toán. Nhưng so sánh với <hoặc> chỉ là tinh ranh trong những tình huống đó. Tuy nhiên, trong trường hợp này, không có cơ hội nào ở quy mô đó không chính xác là 1.0 hoặc 2.0, vì nó là phần cứng được xác định.

Như @fishinear gợi ý, tốt hơn nên sử dụng một cái gì đó như isRetina = [UIScreen mainScreen].scale > 1.95. Điều này cũng sẽ có lợi ích là kiên cường khi @ 4x xuất hiện :)
Danyal Aytekin

Tôi rất không đồng ý. Làm điều này khi không cần thiết làm cho mã ít đọc hơn. Quan điểm về tính chứng minh trong tương lai có thể có hiệu lực của nó, nhưng tôi nghi ngờ rằng chúng ta sẽ sớm có màn hình @ 4x bất cứ lúc nào (nếu có).
Ricardo Sanchez-Saez

Sai lầm. chỉ vì nó là "phần cứng được xác định", theo bất kỳ cách nào, không có nghĩa là bạn tránh được vấn đề so sánh nổi. (Nó chỉ là một float như bất kỳ khác.) Giống như bất kỳ float nào, nói chung bạn không bao giờ có thể sử dụng ==, bạn phải sử dụng một> hoặc <so sánh. Điều gì về> 1,5 cho chắc chắn.
Fattie

2

Đây là một câu riff về câu trả lời của Matt MC ở trên. Chỉ là một thể loại trên 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
Tôi nghi ngờ rằng bộ nhớ đệm alreadyCheckedlà vô cớ, nhưng nó ổn.
Dan Rosenstark

@NikolayShubenkov đó là lý do tại sao tôi đã đặt trước. Trong trường hợp xấu nhất, bạn chạy mã để kiểm tra thêm một hoặc hai thời gian.
Dan Rosenstark

Ý tôi là khi một quá trình sẽ cố gắng kiểm tra trong khi quá trình khác hiện đang đọc ứng dụng giá trị này có thể bị sập. Tôi sẽ thêm dòng đó: @synyncyze (yetChecked) {yetChecked = YES}
Nikolay Shubenkov

2

Phiên bản Swift của các câu trả lời ở trên, với tỷ lệ> = 2.0 để bao gồm iPhone 6+ và các thiết bị khác trong tương lai có tỷ lệ Retina cao hơn:

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

1

Chỉ cần kết hợp câu trả lời từ @sickp và nhận xét sau đây từ @ n13, tôi đã biến nó thành một thể loại UIScreen có vẻ hoạt động tốt. Kiểm tra được thực hiện lần đầu tiên bạn gọi nó và sau đó được lưu cho các cuộc gọi sau.

@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

Có thể hữu ích cho ai đó.


Cảm ơn mã bộ nhớ đệm. Đề nghị duy nhất của tôi là làm cho điều này (Util)thay vì (RetinaCheck)... có lẽ nó không rõ ràng, nhưng nó cho vay để sử dụng khác. Ngoài ra tôi sẽ đặt tên cho phương thức isRetinaDisplayhoặc một cái gì đó bắt đầu bằng is, nhưng có lẽ tôi chưa bao giờ hiểu các hướng dẫn cho Obj-C. Ngoài ra, tôi là một người hâm mộ > 1.0nhưng ai biết điều gì sẽ có ý nghĩa tiến về phía trước.
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;
}

Giải pháp tốt nhất như tôi nghĩ.
Nikolay Shubenkov

0

thử cái này

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

Phiên bản sửa đổi của primulaveris để đơn giản cho hầu hết các trường hợp sử dụng phổ biến. Tôi đang trên 2.2 nhưng điều đó không thành vấn đề.

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

Sau đó, chỉ cần sử dụng chúng như thế này

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

0

Điều này làm việc cho tôi

if((UIScreen .mainScreen().scale) < 2.0)
{
    NSLog("no retina");
}
else
{
    NSLog("retina");
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.