Periksa apakah UIColor gelap atau terang?

107

Saya perlu menentukan apakah UIColor yang dipilih (dipilih oleh pengguna) gelap atau cerah, jadi saya bisa mengubah warna baris teks yang berada di atas warna itu, agar lebih mudah dibaca.

Berikut adalah contoh di Flash / Actionscript (dengan demo): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

Ada pemikiran?

Salam, Andre

MEMPERBARUI

Terima kasih atas saran semua orang, inilah kode yang berfungsi:

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

Sekali lagi terima kasih :)

Andre
sumber
Sepertinya beberapa warna tidak memiliki 3 komponen, seperti UIColor.black.
Glenn Howes

Jawaban:

75

W3C memiliki yang berikut ini: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Jika Anda hanya membuat teks hitam atau putih, gunakan kalkulasi kecerahan warna di atas. Jika di bawah 125, gunakan teks putih. Jika 125 atau lebih, gunakan teks hitam.

edit 1: bias terhadap teks hitam. :)

sunting 2: Rumus yang digunakan adalah ((Nilai merah * 299) + (Nilai hijau * 587) + (Nilai biru * 114)) / 1000.

Erik Nedwidek
sumber
tahukah Anda cara mendapatkan nilai warna merah, hijau, dan biru?
Andre
Anda bisa menggunakan NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);untuk mendapatkan larik komponen warna, termasuk alfa. Dokumen tidak menentukan urutannya tetapi saya berasumsi itu akan menjadi merah, hijau, biru, alfa. Juga, dari dokumen, "ukuran larik adalah satu lebih banyak dari jumlah komponen ruang warna untuk warna." - tidak
disebutkan
Aneh ... menggunakan: NSArray * components = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "komponen warna saya% f% f% f% f", komponen [0], komponen [1], komponen [2], komponen [3]); hanya untuk melihat apakah saya bisa mendapatkan nilainya, tampaknya hanya indeks 0 yang berubah, yang lain akan tetap sama, terlepas dari warna apa yang saya pilih. Tahu kenapa?
Andre
Mengerti. Anda tidak dapat menggunakan NSArray. Gunakan ini sebagai gantinya: const CGFloat * components = CGColorGetComponents (newColor.CGColor); Juga, urutannya adalah: merah adalah komponen [0]; hijau adalah komponen [1]; biru adalah komponen [2]; alpha adalah komponen [3];
Andre
Ah, kesalahanku. Saya pikir itu mengembalikan CFArrayRef, bukan array C. Maaf!
Jasarien
44

Berikut adalah ekstensi Swift (3) untuk melakukan pemeriksaan ini.

Ekstensi ini berfungsi dengan warna abu-abu. Namun, jika Anda membuat semua warna dengan penginisialisasi RGB dan tidak menggunakan warna bawaan seperti UIColor.blackdan UIColor.white, maka Anda dapat menghapus pemeriksaan tambahan.

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

Tes:

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

Catatan: Diperbarui ke Swift 3 12/7/18

josh-fuggle
sumber
6
Bekerja dengan Baik. Dengan Swift 2.0 - Saya mendapat kesalahan kompiler untuk perhitungan kecerahan: "Ekspresi terlalu rumit untuk diselesaikan dalam waktu yang wajar; pertimbangkan untuk memecah ekspresi menjadi sub-ekspresi yang berbeda". Saya memperbaikinya dengan menambahkan jenis: "biarkan kecerahan = (((komponen [0] * 299.0) sebagai CGFloat) + ((komponen [1] * 587.0) sebagai CGFloat) + ((komponen [2] * 114.0)) sebagai CGFloat ) / (1000.0 sebagai CGFloat) "
GK100
1
Saya memiliki masalah yang sama dengan Swift 2.1. Saya memecahkan masalah ini hanya dengan membuat variabel untuk komponen alih-alih mengaksesnya components[0]. Seperti:let componentColorX: CGFloat = components[1]
petritz
1
Xcode memberi tahu saya brightness = "Ekspresi terlalu rumit untuk diselesaikan dalam waktu yang wajar; pertimbangkan untuk memecah ekspresi menjadi sub-ekspresi yang berbeda"
daidai
daidai, sederhanakan ekspresinya. Pembagian di akhir tidak diperlukan. Kita tidak perlu bekerja dalam kisaran 0 - 1. Jangan bagi dengan 1000, lalu periksa saja apakah nilainya lebih besar dari 500.
mulai
32

Dengan menggunakan jawaban Erik Nedwidek, saya mendapatkan potongan kecil kode itu untuk memudahkan penyertaan.

- (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];
}
Remy Vanherweghem
sumber
Saya menggunakan kode ini, bekerja dengan sempurna, tetapi tidak tahu kapan saya menyetel [UIColor blackColor], ia mengembalikan darknessScore = 149.685. Bisakah Anda menjelaskan mengapa ini terjadi?
DivineDesert
3
Kode ini tidak akan berfungsi dengan warna non-RGB seperti UIColor whiteColor, grayColor, blackColor.
rmaddy
@rmaddy kenapa begitu? atau mengapa warna whiteColor, grayColor dan blackColor RGB tidak?
mattsven
1
@rmaddy Bagaimana cara mengetahui secara terprogram perbedaan antara warna dalam ruang warna RGB vs. skala abu-abu?
mattsven
1
@maddahhh! Terima kasih atas bantuannya - 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
}
neoneye
sumber
Bagi saya ini yang terbaik. Anda bahkan dapat mengatur kisaran warna putih di cek untuk menyesuaikan dengan kebutuhan Anda dan sangat sederhana dan asli. Terima kasih.
Andreas777
9

Versi 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)
    }
}
Kaiyuan Xu
sumber
1
Itu mungkin tidak akan berfungsi untuk warna sistem default seperti UIColor.white karena hanya memiliki 2 komponen, ini bukan representasi RGB.
Skrew
7

Solusi saya untuk masalah ini dalam kategori (diambil dari jawaban lain di sini). Juga dapat digunakan dengan warna skala abu-abu, yang pada saat penulisan ini tidak ada jawaban lain yang berfungsi.

@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
mattsven
sumber
penanganan yang bagus dari warna non RGB - berfungsi seperti pesona.
hatunike
5

Ekstensi Swift 3 yang lebih sederhana:

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
    }
}
Sunkas
sumber
2
Itu mungkin tidak akan berfungsi untuk warna sistem default seperti UIColor.white karena hanya memiliki 2 komponen, ini bukan representasi RGB.
Skrew
Benar! Anda dapat mengubah warna menjadi string hex dan kemudian kembali ke UIColor untuk menjelaskannya.
Sunkas
3

Jika Anda lebih suka versi blok:

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;
};
kakilangit
sumber
2

Untuk segala sesuatu yang tidak keabu-abuan, kebalikan RGB suatu warna biasanya sangat kontras dengannya. Demo hanya membalikkan warna dan desaturasi (mengubahnya menjadi abu-abu).

Tetapi menghasilkan kombinasi warna yang menenangkan cukup rumit. Melihat :

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

rep_movsd
sumber
1
Andai saja ada versi iPhone itu: D
Andre
Jadi jika saya mendapatkan nilai RGB dari suatu warna (yang saya tidak tahu bagaimana melakukannya) dan membuat warna baru dengan kebalikan dari nilai-nilai itu, yang seharusnya bekerja pada tingkat yang sangat dasar?
Andre
2

Metode berikut adalah menemukan warna terang atau gelap dalam bahasa Swift berdasarkan warna putih.

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
}

Metode berikut adalah menemukan warna terang atau gelap di Swift menggunakan komponen warna.

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
}
abhi
sumber
2

Bagi saya yang hanya menggunakan CGColorGetComponents tidak berfungsi, saya mendapatkan 2 komponen untuk UIColors seperti putih. Jadi saya harus memeriksa color spaceModel terlebih dahulu. Inilah yang saya temukan yang akhirnya menjadi versi cepat dari jawaban @ mattsven.

Ruang warna diambil dari sini: 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
    }
Lucho
sumber
2

UIColor memiliki metode berikut untuk mengonversi ke ruang warna HSB :

- (BOOL)getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha;
Kamar kecil
sumber
1
- (BOOL)isColorLight:(UIColor*)color
{
    CGFloat white = 0;
    [color getWhite:&white alpha:nil];
    return (white >= .85);
}

Swift 5Versi yang ditambahkan :

var white: CGFloat = 0.0
color.getWhite(&white, alpha: nil)
return white >= .85 // Don't use white background
Mike Glukhov
sumber
1
Ini hanya berfungsi jika UIColorwarnanya grayscale. Warna RGB acak tidak akan berfungsi dengan kode ini.
rmaddy
0

Jika Anda ingin mengetahui kecerahan warna, berikut beberapa pseudo code:

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

Jika> 0,5 berarti cerah, dan sebaliknya gelap.

Bryan Denny
sumber
2
Anda tidak dapat memberi bobot yang sama pada setiap warna. Mata kita memiliki bias terhadap warna biru dan sedikit merah.
Erik Nedwidek
@ErikNedwidek: Cukup adil, pertanyaannya hanya menanyakan kecerahan warna :)
Bryan Denny