UIColor'un koyu mu parlak mı olduğunu kontrol edin.


107

Seçili bir UIColor'un (kullanıcı tarafından seçilen) koyu mu yoksa parlak mı olduğunu belirlemem gerekiyor, böylece daha iyi okunabilirlik için o rengin üstünde duran bir metin satırının rengini değiştirebilirim.

Flash / Actionscript'te (demolu) bir örnek: http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

Düşüncesi olan var mı?

Şerefe, Andre

GÜNCELLEME

Herkesin önerileri sayesinde, işte çalışma kodu:

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

Tekrar teşekkürler :)


Görünüşe göre bazı renklerin UIColor.black gibi 3 bileşeni yok.
Glenn Howes

Yanıtlar:


75

W3C'de şunlar bulunur: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Yalnızca siyah veya beyaz metin yapıyorsanız, yukarıdaki renk parlaklığı hesaplamasını kullanın. 125'in altındaysa beyaz metin kullanın. 125 veya üzeriyse siyah metin kullanın.

düzenleme 1: siyah metne doğru önyargı. :)

düzenleme 2: Kullanılacak formül ((Kırmızı değer * 299) + (Yeşil değer * 587) + (Mavi değer * 114)) / 1000'dir.


bir rengin kırmızı, yeşil ve mavi değerlerini nasıl elde edeceğinizi biliyor musunuz?
Andre

NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);Alfa da dahil olmak üzere bir dizi renk bileşeni elde etmek için kullanabilirsiniz . Dokümanlar hangi sırada olduklarını belirtmiyor, ancak kırmızı, yeşil, mavi, alfa olacağını varsayıyorum. Ayrıca, belgelerden "dizinin boyutu, renk için renk uzayının bileşen sayısından bir fazladır." - nedenini söylemiyor ...
Jasarien

Bu garip ... kullanım: NSArray * bileşenleri = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "renk bileşenlerim% f% f% f% f", bileşenler [0], bileşenler [1], bileşenler [2], bileşenler [3]); Sadece değerleri alıp alamayacağımı görmek için, sadece 0 indeksi değişiyor gibi görünüyor, diğerleri hangi rengi seçersem seçeyim aynı kalacak. Herhangi bir fikrin neden?
Andre

Anladım. NSArray kullanamazsınız. Bunun yerine şunu kullanın: const CGFloat * components = CGColorGetComponents (newColor.CGColor); Ayrıca sıra şöyledir: kırmızı [0] bileşenleridir; yeşil [1] bileşenleridir; mavi [2] bileşenidir; alfa [3] bileşenleridir;
Andre

Ah, benim hatam. Bir C dizisi değil, bir CFArrayRef döndürdüğünü düşündüm. Afedersiniz!
Jasarien

44

İşte bu kontrolü gerçekleştirmek için bir Swift (3) uzantısı.

Bu uzantı gri tonlamalı renklerle çalışır. Ancak, RGB ilklendiricili tüm renkleri oluşturma ve kullanmıyorsanız gibi renklerde inşa UIColor.blackve UIColor.whiteardından muhtemelen size ek kontroller kaldırabilirsiniz.

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

Testler:

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

Not: Swift 3'e güncellendi 12/7/18


6
Harika çalışıyor. Swift 2.0 ile - parlaklık hesaplaması için bir derleyici hatası aldım: "İfade makul bir sürede çözülemeyecek kadar karmaşıktı; ifadeyi farklı alt ifadelere ayırmayı düşünün". Bunu şu türü ekleyerek düzelttim: "parlaklık = (((bileşenleri [0] * 299.0) CGFloat olarak) + ((bileşenler [1] * 587.0) CGFloat olarak) + ((bileşenler [2] * 114.0)) CGFloat olarak CGFloat gibi) / (1000.0)"
GK100

1
Swift 2.1 ile aynı sorunu yaşadım. Bu sorunu, bileşenlere erişmek yerine bileşenler için değişkenler oluşturarak çözdüm components[0]. Öyle gibi:let componentColorX: CGFloat = components[1]
petritz

1
Xcode bana parlaklığı söylüyor = "İfade makul bir zamanda çözülemeyecek kadar karmaşıktı; ifadeyi farklı alt ifadelere
ayırmayı

daidai, sadece ifadeyi basitleştirin. Sondaki bölünme gereksizdir. 0 - 1 aralığında çalışmamıza gerek yok. 1000'e bölmeyin, bunun yerine değerin 500'den büyük olup olmadığını kontrol edin.
Aronspring

32

Erik Nedwidek'in cevabını kullanarak, kolay dahil etmek için o küçük kod parçacığını buldum.

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

Bu kodu kullandım, mükemmel çalıştı, ancak [UIColor blackColor] ayarladığımda, darkScore = 149.685 döndürdüğünü bilmiyorum. Bunun neden olduğunu açıklayabilir misin?
DivineDesert

3
Bu kod, RGB olmayan renklerle çalışmaz UIColor whiteColor, grayColor, blackColor.
rmaddy

@rmaddy neden bu? veya neden whiteColor, grayColor ve blackColor RGB renkleri değil?
mattsven

1
@rmaddy RGB renk alanı ile gri tonlama arasındaki renkler arasındaki farkı programlı olarak nasıl anlayabilirim?
mattsven

1
@maddy Nevermind! Yardım için teşekkürler - 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
}

Benim için bu en iyisi. Çekin içindeki beyaz aralığını ihtiyaçlarınıza uyacak şekilde bile ayarlayabilirsiniz ve süper basit ve doğaldır. Teşekkürler.
Andreas777

9

Swift 4 Sürümü

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
Bu muhtemelen UIColor.white gibi varsayılan sistem renkleri için çalışmayacaktır, çünkü yalnızca 2 bileşeni olacaktır, RGB temsili değildir.
Skrew

7

Bu soruna bir kategorideki çözümüm (buradaki diğer cevaplardan alınmıştır). Ayrıca gri tonlamalı renklerle de çalışır; bu, yazarken diğer yanıtların hiçbirinin yapmadığıdır.

@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 olmayan renklerin hoş kullanımı - bir cazibe gibi çalışır.
hatunike

5

Simpler Swift 3 uzantısı:

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
Bu muhtemelen UIColor.white gibi varsayılan sistem renkleri için çalışmayacaktır, çünkü yalnızca 2 bileşeni olacaktır, RGB temsili değildir.
Skrew

Doğru! Rengi onaltılık dizeye dönüştürebilir ve ardından bunu hesaba katmak için UIColor'a geri dönebilirsiniz.
Sunkas

3

Blok versiyonunu tercih ederseniz:

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

Grimsi olmayan her şey için, bir rengin RGB tersi genellikle onunla oldukça zıttır. Demo sadece rengi tersine çevirir ve doygunluğunu giderir (griye dönüştürür).

Ancak rahatlatıcı bir renk kombinasyonu oluşturmak oldukça karmaşıktır. Bakmak :

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


1
Keşke bunun bir iPhone sürümü olsaydı: D
Andre

Öyleyse, bir rengin RGB değerlerini (nasıl yapacağımı bilmediğim) aldıysam ve bu değerlerin tersiyle yeni bir renk yarattıysam, bu çok basit bir seviyede çalışmalı mı?
Andre

2

Aşağıdaki yöntem, Swift dilinde beyazın rengine göre rengin açık veya koyu olduğunu bulmaktır.

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
}

Aşağıdaki yöntem, renk bileşenlerini kullanarak Swift'de rengin açık veya koyu olduğunu bulmaktır.

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

Sadece CGColorGetComponents kullanmam için işe yaramadı, beyaz gibi UIColors için 2 bileşen alıyorum. Bu yüzden önce renk uzay modelini kontrol etmem gerekiyor. @ Mattsven'in cevabının hızlı versiyonu olarak ortaya çıktığım şey buydu.

Buradan alınan renk alanı: 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 renk uzayına dönüştürmek için aşağıdaki yönteme sahiptir :

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

Eklenen Swift 5sürüm:

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

1
Bu yalnızca UIColorgri tonlamalı bir renkse işe yarar . Rastgele bir RGB rengi bu kodla çalışmaz.
rmaddy

0

Rengin parlaklığını bulmak istiyorsanız, işte bazı sözde kod:

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 ise parlak, aksi takdirde karanlıktır.


2
Renklerin her birine eşit ağırlık veremezsiniz. Gözlerimiz maviye ve daha az ölçüde kırmızıya karşı bir önyargıya sahiptir.
Erik Nedwidek

@ErikNedwidek: Yeterince adil, soru sadece bir rengin parlaklığını sordu :)
Bryan Denny
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.