Kiểm tra xem UIColor tối hay sáng?


107

Tôi cần xác định xem một UIColor đã chọn (do người dùng chọn) là tối hay sáng, vì vậy tôi có thể thay đổi màu của dòng văn bản nằm trên màu đó, để dễ đọc hơn.

Đây là một ví dụ trong Flash / Actionscript (với bản trình diễn): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

Có suy nghĩ gì không?

Chúc mừng, Andre

CẬP NHẬT

Nhờ mọi người góp ý, đây là mã hoạt động:

- (void) updateColor:(UIColor *) newColor
{
    const CGFloat *componentColors = CGColorGetComponents(newColor.CGColor);

    CGFloat colorBrightness = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
    if (colorBrightness < 0.5)
    {
        NSLog(@"my color is dark");
    }
    else
    {
        NSLog(@"my color is light");
    }
}

Một lần nữa xin cảm ơn :)


Có vẻ như một số màu không có 3 thành phần, chẳng hạn như UIColor.black.
Glenn Howes

Câu trả lời:


75

W3C có những điều sau: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Nếu bạn chỉ làm văn bản đen hoặc trắng, hãy sử dụng tính toán độ sáng màu ở trên. Nếu nó dưới 125, hãy sử dụng văn bản màu trắng. Nếu nó là 125 trở lên, hãy sử dụng văn bản màu đen.

chỉnh sửa 1: thiên vị đối với văn bản màu đen. :)

chỉnh sửa 2: Công thức sử dụng là ((Giá trị màu đỏ * 299) + (Giá trị màu xanh lá cây * 587) + (Giá trị màu xanh lam * 114)) / 1000.


bạn có biết cách lấy các giá trị đỏ, lục và lam của một màu không?
Andre

Bạn có thể sử dụng NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);để lấy một mảng các thành phần màu, bao gồm cả alpha. Các tài liệu không chỉ định thứ tự của chúng nhưng tôi cho rằng nó sẽ là đỏ, lục, lam, alpha. Ngoài ra, từ tài liệu, "kích thước của mảng nhiều hơn một số thành phần của không gian màu cho màu." - nó không nói tại sao ...
Jasarien

Thật kỳ lạ ... bằng cách sử dụng: NSArray * components = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "các thành phần màu của tôi% f% f% f% f", thành phần [0], thành phần [1], thành phần [2], thành phần [3]); chỉ để xem liệu tôi có thể nhận được các giá trị hay không, có vẻ như chỉ có 0 thay đổi, các chỉ số khác sẽ giữ nguyên, bất kể tôi chọn màu gì. Bất kỳ ý tưởng tại sao?
Andre

Hiểu rồi. Bạn không thể sử dụng NSArray. Sử dụng điều này thay thế: const CGFloat * components = CGColorGetComponents (newColor.CGColor); Ngoài ra, thứ tự là: red là thành phần [0]; xanh lục là các thành phần [1]; xanh lam là các thành phần [2]; alpha là các thành phần [3];
Andre

A, tệ hại của tôi. Tôi nghĩ rằng nó trả về một CFArrayRef, không phải một mảng C. Lấy làm tiếc!
Jasarien

44

Đây là một phần mở rộng Swift (3) để thực hiện việc kiểm tra này.

Tiện ích mở rộng này hoạt động với các màu thang độ xám. Tuy nhiên, nếu bạn đang tạo tất cả các màu của mình bằng bộ khởi tạo RGB và không sử dụng các màu tích hợp như UIColor.blackUIColor.white, thì bạn có thể xóa các kiểm tra bổ sung.

extension UIColor {

    // Check if the color is light or dark, as defined by the injected lightness threshold.
    // Some people report that 0.7 is best. I suggest to find out for yourself.
    // A nil value is returned if the lightness couldn't be determined.
    func isLight(threshold: Float = 0.5) -> Bool? {
        let originalCGColor = self.cgColor

        // Now we need to convert it to the RGB colorspace. UIColor.white / UIColor.black are greyscale and not RGB.
        // If you don't do this then you will crash when accessing components index 2 below when evaluating greyscale colors.
        let RGBCGColor = originalCGColor.converted(to: CGColorSpaceCreateDeviceRGB(), intent: .defaultIntent, options: nil)
        guard let components = RGBCGColor?.components else {
            return nil
        }
        guard components.count >= 3 else {
            return nil
        }

        let brightness = Float(((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000)
        return (brightness > threshold)
    }
}

Kiểm tra:

func testItWorks() {
    XCTAssertTrue(UIColor.yellow.isLight()!, "Yellow is LIGHT")
    XCTAssertFalse(UIColor.black.isLight()!, "Black is DARK")
    XCTAssertTrue(UIColor.white.isLight()!, "White is LIGHT")
    XCTAssertFalse(UIColor.red.isLight()!, "Red is DARK")
}

Lưu ý: Đã cập nhật lên Swift 3 12/7/18


6
Hoạt động tuyệt vời. Với Swift 2.0 - Tôi gặp lỗi trình biên dịch đối với tính toán độ sáng: "Biểu thức quá phức tạp để được giải quyết trong thời gian hợp lý; hãy xem xét chia nhỏ biểu thức thành các biểu thức con riêng biệt". Tôi đã sửa lỗi này bằng cách thêm kiểu: "let Bright = (((components [0] * 299.0) as CGFloat) + ((components [1] * 587.0) as CGFloat) + ((components [2] * 114.0)) as CGFloat ) / (1000.0 dưới dạng CGFloat) "
GK100,

1
Tôi đã gặp vấn đề tương tự với Swift 2.1. Tôi đã giải quyết vấn đề này bằng cách chỉ cần tạo các biến cho các thành phần thay vì truy cập chúng bằng components[0]. Như vậy:let componentColorX: CGFloat = components[1]
petritz

1
Xcode nói với tôi độ sáng = "Biểu hiện là quá phức tạp để được giải quyết trong thời gian hợp lý; xem xét phá vỡ sự biểu hiện thành rõ rệt tiểu biểu"
daidai

daidai, chỉ cần đơn giản hóa biểu thức. Sự phân chia ở cuối là không cần thiết. Chúng ta không cần phải làm việc trong phạm vi 0 - 1. Không chia cho 1000, sau đó chỉ cần kiểm tra xem giá trị có lớn hơn 500 hay không.
aronspring,

32

Sử dụng câu trả lời của Erik Nedwidek, tôi nghĩ ra đoạn mã nhỏ đó để dễ dàng đưa vào.

- (UIColor *)readableForegroundColorForBackgroundColor:(UIColor*)backgroundColor {
    size_t count = CGColorGetNumberOfComponents(backgroundColor.CGColor);
    const CGFloat *componentColors = CGColorGetComponents(backgroundColor.CGColor);

    CGFloat darknessScore = 0;
    if (count == 2) {
        darknessScore = (((componentColors[0]*255) * 299) + ((componentColors[0]*255) * 587) + ((componentColors[0]*255) * 114)) / 1000;
    } else if (count == 4) {
        darknessScore = (((componentColors[0]*255) * 299) + ((componentColors[1]*255) * 587) + ((componentColors[2]*255) * 114)) / 1000;
    }

    if (darknessScore >= 125) {
        return [UIColor blackColor];
    }

    return [UIColor whiteColor];
}

Tôi đã sử dụng mã này, hoạt động hoàn hảo, nhưng không biết khi tôi đặt [UIColor blackColor], nó trả về darkScore = 149,685. Bạn có thể giải thích tại sao điều này đang xảy ra?
DivineDesert

3
Mã này sẽ không hoạt động với các màu không phải RGB chẳng hạn như UIColor whiteColor, grayColor, blackColor.
rmaddy

@rmaddy tại sao vậy? hoặc tại sao không có màu trắngColor, màu xám và màu đenColor RGB?
mattsven

1
@rmaddy Làm cách nào để tôi có thể lập trình phân biệt sự khác biệt giữa các màu trong không gian màu RGB so với thang độ xám?
mattsven

1
@maddy Nevermind! Cảm ơn sự giúp đỡ - stackoverflow.com/questions/1099569/…
mattsven

31

Swift3

extension UIColor {
    var isLight: Bool {
        var white: CGFloat = 0
        getWhite(&white, alpha: nil)
        return white > 0.5
    }
}

// Usage
if color.isLight {
    label.textColor = UIColor.black
} else {
    label.textColor = UIColor.white
}

Đối với tôi cái này là cái tốt nhất. Bạn thậm chí có thể đặt phạm vi màu trắng trong séc để phù hợp với nhu cầu của mình và cực kỳ đơn giản và tự nhiên. Cảm ơn.
Andreas777

9

Phiên bản Swift 4

extension UIColor {
    func isLight() -> Bool {
        guard let components = cgColor.components, components.count > 2 else {return false}
        let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
        return (brightness > 0.5)
    }
}

1
Điều đó có thể sẽ không hoạt động đối với các màu hệ thống mặc định như UIColor.white vì nó sẽ chỉ có 2 thành phần, nó không phải là đại diện RGB.
Skrew

7

Giải pháp của tôi cho vấn đề này trong một danh mục (được rút ra từ các câu trả lời khác ở đây). Cũng hoạt động với màu xám, mà tại thời điểm viết không có câu trả lời nào khác làm được.

@interface UIColor (Ext)

    - (BOOL) colorIsLight;

@end

@implementation UIColor (Ext)

    - (BOOL) colorIsLight {
        CGFloat colorBrightness = 0;

        CGColorSpaceRef colorSpace = CGColorGetColorSpace(self.CGColor);
        CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);

        if(colorSpaceModel == kCGColorSpaceModelRGB){
            const CGFloat *componentColors = CGColorGetComponents(self.CGColor);

            colorBrightness = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
        } else {
            [self getWhite:&colorBrightness alpha:0];
        }

        return (colorBrightness >= .5f);
    }

@end

xử lý tốt các màu không phải RGB - hoạt động như một sự quyến rũ.
hatunike

5

Tiện ích mở rộng Swift 3 đơn giản hơn:

extension UIColor {
    func isLight() -> Bool {
        guard let components = cgColor.components else { return false }
        let redBrightness = components[0] * 299
        let greenBrightness = components[1] * 587
        let blueBrightness = components[2] * 114
        let brightness = (redBrightness + greenBrightness + blueBrightness) / 1000
        return brightness > 0.5
    }
}

2
Điều đó có thể sẽ không hoạt động đối với các màu hệ thống mặc định như UIColor.white vì nó sẽ chỉ có 2 thành phần, nó không phải là đại diện RGB.
Skrew

Thật! Bạn có thể chuyển đổi màu thành chuỗi hex và sau đó quay lại UIColor để giải thích điều đó.
Sunkas

3

Nếu bạn thích phiên bản khối:

BOOL (^isDark)(UIColor *) = ^(UIColor *color){
    const CGFloat *component = CGColorGetComponents(color.CGColor);
    CGFloat brightness = ((component[0] * 299) + (component[1] * 587) + (component[2] * 114)) / 1000;

    if (brightness < 0.75)
        return  YES;
    return NO;
};

2

Đối với mọi thứ không có màu xám, nghịch đảo RGB của một màu thường có độ tương phản cao với nó. Bản demo chỉ đảo ngược màu và khử bão hòa (chuyển nó thành màu xám).

Nhưng việc tạo ra một sự kết hợp màu sắc nhẹ nhàng đẹp mắt là khá phức tạp. Nhìn vào :

http://particletree.com/notebook/calculating-color-contrast-for-legible-text/


1
Giá như có một phiên bản iPhone của điều đó: D
Andre

Vì vậy, nếu tôi nhận các giá trị RGB của một màu (mà tôi không biết làm thế nào để làm) và tạo một màu mới với nghịch đảo của các giá trị đó, thì điều đó có hoạt động ở mức rất cơ bản không?
Andre

2

Phương pháp sau là tìm màu sáng hoặc tối trong ngôn ngữ Swift dựa trên màu trắng.

func isLightColor(color: UIColor) -> Bool 
{
   var white: CGFloat = 0.0
   color.getWhite(&white, alpha: nil)

   var isLight = false

   if white >= 0.5
   {
       isLight = true
       NSLog("color is light: %f", white)
   }
   else
   {
      NSLog("Color is dark: %f", white)
   }

   return isLight
}

Phương pháp sau là tìm màu sáng hoặc tối trong Swift bằng cách sử dụng các thành phần màu.

func isLightColor(color: UIColor) -> Bool 
{
     var isLight = false

     var componentColors = CGColorGetComponents(color.CGColor)

     var colorBrightness: CGFloat = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
     if (colorBrightness >= 0.5)
     {
        isLight = true
        NSLog("my color is light")
     }
     else
     {
        NSLog("my color is dark")
     }  
     return isLight
}

2

Đối với tôi chỉ sử dụng CGColorGetComponents không hoạt động, tôi nhận được 2 thành phần cho UIColors như màu trắng. Vì vậy, tôi phải kiểm tra color spaceModel trước. Đây là những gì tôi nghĩ ra và cuối cùng là phiên bản nhanh chóng của câu trả lời của @ mattsven.

Không gian màu được lấy từ đây: https://stackoverflow.com/a/16981916/4905076

extension UIColor {
    func isLight() -> Bool {
        if let colorSpace = self.cgColor.colorSpace {
            if colorSpace.model == .rgb {
                guard let components = cgColor.components, components.count > 2 else {return false}

                let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000

                return (brightness > 0.5)
            }
            else {
                var white : CGFloat = 0.0

                self.getWhite(&white, alpha: nil)

                return white >= 0.5
            }
        }

        return false
    }

2

UIColor có phương pháp sau để chuyển đổi sang không gian màu HSB :

- (BOOL)getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha;

1
- (BOOL)isColorLight:(UIColor*)color
{
    CGFloat white = 0;
    [color getWhite:&white alpha:nil];
    return (white >= .85);
}

Swift 5Phiên bản đã thêm :

var white: CGFloat = 0.0
color.getWhite(&white, alpha: nil)
return white >= .85 // Don't use white background

1
Điều này chỉ hoạt động nếu UIColorlà màu xám. Một màu RGB ngẫu nhiên sẽ không hoạt động với mã này.
rmaddy

0

Nếu bạn muốn tìm độ sáng của màu, đây là một số mã giả:

public float GetBrightness(int red, int blue, int green)
{
    float num = red / 255f;
    float num2 = blue / 255f;
    float num3 = green / 255f;
    float num4 = num;
    float num5 = num;
    if (num2 > num4)
        num4 = num2;
    if (num3 > num4)
        num4 = num3;
    if (num2 < num5)
        num5 = num2;
    if (num3 < num5)
        num5 = num3;
    return ((num4 + num5) / 2f);
}

Nếu nó> 0,5 nó là sáng, và ngược lại là tối.


2
Bạn không thể cân mỗi màu như nhau. Đôi mắt của chúng ta thiên về màu xanh lam và ở mức độ thấp hơn là màu đỏ.
Erik Nedwidek

@ErikNedwidek: Đủ công bằng, câu hỏi chỉ đơn giản hỏi về độ sáng của một màu :)
Bryan Denny
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.