ตรวจสอบว่า UIColor มืดหรือสว่าง?


108

ฉันต้องการตรวจสอบว่า UIColor ที่เลือก (เลือกโดยผู้ใช้) นั้นมืดหรือสว่างฉันจึงสามารถเปลี่ยนสีของบรรทัดข้อความที่อยู่ด้านบนของสีนั้นเพื่อให้อ่านง่ายขึ้น

นี่คือตัวอย่างใน Flash / Actionscript (พร้อมการสาธิต): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

ความคิดใด ๆ ?

ไชโยอังเดร

อัปเดต

ขอบคุณคำแนะนำของทุกคนนี่คือรหัสการทำงาน:

- (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");
    }
}

ขอบคุณอีกครั้ง :)


ดูเหมือนว่าสีบางสีจะไม่มีส่วนประกอบ 3 อย่างเช่น UIColor.black
Glenn Howes

คำตอบ:


75

W3C มีดังต่อไปนี้: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

หากคุณทำเฉพาะข้อความสีดำหรือสีขาวให้ใช้การคำนวณความสว่างของสีด้านบน หากต่ำกว่า 125 ให้ใช้ข้อความสีขาว หากเป็น 125 ขึ้นไปให้ใช้ข้อความสีดำ

แก้ไข 1: อคติต่อข้อความสีดำ :)

แก้ไข 2: สูตรที่จะใช้คือ ((ค่าสีแดง * 299) + (ค่าสีเขียว * 587) + (ค่าสีน้ำเงิน * 114)) / 1,000


คุณรู้วิธีรับค่าสีแดงเขียวและน้ำเงินของสีหรือไม่?
Andre

คุณสามารถใช้NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);เพื่อรับอาร์เรย์ของส่วนประกอบสีรวมทั้งอัลฟา เอกสารไม่ได้ระบุว่าเรียงลำดับอะไร แต่ฉันคิดว่ามันจะเป็นสีแดงเขียวน้ำเงินอัลฟ่า นอกจากนี้จากเอกสาร "ขนาดของอาร์เรย์เป็นมากกว่าจำนวนส่วนประกอบของพื้นที่สีสำหรับสี" - ไม่บอกว่าทำไม ...
Jasarien

มันแปลก ... โดยใช้: NSArray * components = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "ส่วนประกอบสีของฉัน% f% f% f% f", ส่วนประกอบ [0], ส่วนประกอบ [1], ส่วนประกอบ [2], ส่วนประกอบ [3]); เพียงเพื่อดูว่าฉันสามารถรับค่าได้หรือไม่ดูเหมือนว่าดัชนี 0 เท่านั้นที่เปลี่ยนไปส่วนอื่น ๆ จะยังคงเหมือนเดิมไม่ว่าฉันจะเลือกสีอะไรก็ตาม คิดว่าทำไม?
Andre

เข้าใจแล้ว คุณไม่สามารถใช้ NSArray ใช้สิ่งนี้แทน: const CGFloat * components = CGColorGetComponents (newColor.CGColor); นอกจากนี้คำสั่งคือ: สีแดงคือส่วนประกอบ [0]; สีเขียวเป็นส่วนประกอบ [1]; สีน้ำเงินเป็นส่วนประกอบ [2]; อัลฟาเป็นส่วนประกอบ [3];
Andre

อาไม่ดีของฉัน ฉันคิดว่ามันส่งคืน CFArrayRef ไม่ใช่อาร์เรย์ C ขออภัย!
Jasarien

44

นี่คือส่วนขยาย Swift (3) เพื่อทำการตรวจสอบนี้

ส่วนขยายนี้ใช้งานได้กับสีเทา อย่างไรก็ตามหากคุณกำลังสร้างสีทั้งหมดของคุณด้วยตัวเริ่มต้น RGB และไม่ได้ใช้สีในตัวเช่นUIColor.blackและUIColor.whiteคุณสามารถลบการตรวจสอบเพิ่มเติมได้

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

การทดสอบ:

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

หมายเหตุ: อัปเดตเป็น Swift 3 12/7/18


6
ใช้งานได้ดี ด้วย Swift 2.0 - ฉันได้รับข้อผิดพลาดของคอมไพเลอร์สำหรับการคำนวณความสว่าง: "นิพจน์ซับซ้อนเกินกว่าจะแก้ไขได้ในเวลาอันสมควรลองพิจารณาแยกนิพจน์ออกเป็นนิพจน์ย่อยที่แตกต่างกัน" ฉันแก้ไขโดยการเพิ่มประเภท: "ให้ความสว่าง = (((ส่วนประกอบ [0] * 299.0) เป็น CGFloat) + ((ส่วนประกอบ [1] * 587.0) เป็น CGFloat) + ((ส่วนประกอบ [2] * 114.0)) เป็น CGFloat ) / (1000.0 เป็น CGFloat) "
GK100

1
ฉันมีปัญหาเดียวกันกับ Swift 2.1 ฉันแก้ไขปัญหานี้โดยสร้างตัวแปรสำหรับส่วนประกอบแทนที่จะเข้าถึงด้วยcomponents[0]. ดังนี้:let componentColorX: CGFloat = components[1]
petritz

1
Xcode บอกความสว่าง = "นิพจน์ซับซ้อนเกินกว่าจะแก้ไขได้ในเวลาอันสมควรลองแยกนิพจน์ออกเป็นนิพจน์ย่อยที่แตกต่างกัน"
daidai

daidai เพียงแค่ทำให้นิพจน์ง่ายขึ้น การแบ่งส่วนท้ายไม่จำเป็น เราไม่จำเป็นต้องทำงานในช่วง 0 - 1 อย่าหารด้วย 1000 แล้วตรวจสอบว่าค่ามากกว่า 500 แทนหรือไม่
aronspring

32

ด้วยการใช้คำตอบของ Erik Nedwidek ฉันจึงได้ข้อมูลโค้ดเล็ก ๆ น้อย ๆ เพื่อให้รวมเข้าได้ง่าย

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

ฉันใช้รหัสนี้ทำงานได้อย่างสมบูรณ์ แต่ไม่รู้ว่าเมื่อฉันตั้งค่า [UIColor blackColor] มันจะคืนค่า darkScore = 149.685 คุณช่วยอธิบายได้ไหมว่าเหตุใดจึงเกิดขึ้น
DivineDesert

3
รหัสนี้ใช้ไม่ได้กับสีที่ไม่ใช่ RGB เช่นUIColor whiteColor, grayColor, blackColor.
rmaddy

@rmaddy ทำไมถึงเป็นอย่างนั้น? หรือทำไมไม่เป็นสี whiteColor, greyColor และ blackColor RGB?
mattsven

1
@rmaddy ฉันจะบอกความแตกต่างระหว่างสีในพื้นที่สี RGB กับระดับสีเทาโดยทางโปรแกรมได้อย่างไร
mattsven

1
@maddy ไม่เป็นไร! ขอบคุณสำหรับความช่วยเหลือ - stackoverflow.com/questions/1099569/…
mattsven

32

สวิฟท์ 3

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
}

สำหรับฉันอันนี้ดีที่สุด คุณยังสามารถกำหนดช่วงของสีขาวในการตรวจสอบเพื่อให้เหมาะกับความต้องการของคุณและเรียบง่ายและเป็นธรรมชาติ ขอบคุณ.
Andreas777

9

เวอร์ชัน 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
นั่นอาจใช้ไม่ได้กับสีเริ่มต้นของระบบเช่น UIColor.white เนื่องจากมีส่วนประกอบเพียง 2 ส่วนไม่ใช่การแสดง RGB
Skrew

7

วิธีแก้ปัญหาของฉันในหมวดหมู่ (ดึงมาจากคำตอบอื่น ๆ ที่นี่) ยังใช้งานได้กับสีโทนเทาซึ่งในขณะที่เขียนไม่มีคำตอบอื่นใดทำได้

@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

จัดการกับสีที่ไม่ใช่ RGB ได้ดี - ใช้งานได้ดีเหมือนมีเสน่ห์
hatunike

5

ส่วนขยาย Swift 3 ที่ง่ายกว่า:

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
นั่นอาจใช้ไม่ได้กับสีเริ่มต้นของระบบเช่น UIColor.white เนื่องจากมีส่วนประกอบเพียง 2 ส่วนไม่ใช่การแสดง RGB
Skrew

จริง! คุณสามารถแปลงสีเป็นสตริงเลขฐานสิบหกแล้วกลับไปที่ UIColor เพื่อพิจารณาสิ่งนั้น
Sunkas

3

หากคุณต้องการเวอร์ชันบล็อก:

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

สำหรับทุกสิ่งที่ไม่เป็นสีเทา RGB ผกผันของสีมักจะตัดกันอย่างมาก การสาธิตเพียงแค่เปลี่ยนสีและทำให้อิ่มตัว (แปลงเป็นสีเทา)

แต่การสร้างการผสมผสานของสีที่ดีนั้นค่อนข้างซับซ้อน ดูที่ :

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


1
หากมีเพียง iPhone รุ่นนั้น: D
Andre

ดังนั้นถ้าฉันได้ค่า RGB ของสี (ซึ่งฉันไม่รู้ว่าต้องทำอย่างไร) และสร้างสีใหม่โดยมีค่าผกผันของค่าเหล่านั้นมันควรจะใช้ได้ในระดับพื้นฐานหรือไม่?
Andre

2

วิธีการต่อไปนี้คือค้นหาว่าสีอ่อนหรือเข้มในภาษา Swift ตามสีขาว

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
}

วิธีต่อไปนี้คือค้นหาว่าสีอ่อนหรือเข้มใน Swift โดยใช้ส่วนประกอบสี

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

สำหรับฉันที่ใช้ CGColorGetComponents เพียงอย่างเดียวไม่ได้ผลฉันได้รับ 2 องค์ประกอบสำหรับ UIColors เช่นสีขาว เลยต้องตรวจสอบ color spaceModel ก่อน นี่คือสิ่งที่ฉันคิดขึ้นมาซึ่งกลายเป็นคำตอบของ @mattsven เวอร์ชันที่รวดเร็ว

พื้นที่สีนำมาจากที่นี่: 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 มีวิธีการต่อไปนี้ในการแปลงเป็นพื้นที่สี 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 5เวอร์ชันที่เพิ่ม:

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

1
จะใช้ได้เฉพาะในกรณีที่UIColorสีเป็นสีเทา สี RGB แบบสุ่มจะใช้ไม่ได้กับรหัสนี้
rmaddy

0

หากคุณต้องการค้นหาความสว่างของสีนี่คือรหัสเทียมบางส่วน:

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

ถ้าเป็น> 0.5 จะสว่างหรือมืด


2
คุณไม่สามารถให้น้ำหนักแต่ละสีเท่ากันได้ ดวงตาของเรามีอคติกับสีน้ำเงินและมีสีแดงน้อยกว่า
Erik Nedwidek

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