从 UIColor 获得稍微更亮和更暗的颜色

Posted

技术标签:

【中文标题】从 UIColor 获得稍微更亮和更暗的颜色【英文标题】:Get Slightly Lighter and Darker Color from UIColor 【发布时间】:2012-07-20 20:00:27 【问题描述】:

我希望能够将任何 UIColor 转换为渐变。我打算这样做的方式是使用 Core Graphics 绘制渐变。我想做的是获得一种颜色,让我们说:

[UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];

并得到一个 UIColor ,它更暗一些,更亮一些。有谁知道如何做到这一点?谢谢。

【问题讨论】:

“渐变”意味着图像的一部分将是一种颜色的阴影,而另一部分将是较暗或较浅的阴影。这是另一种定义你想要做什么的方式吗? 看看这个库github.com/yannickl/DynamicColor Swift 使用色调饱和度和亮度***.com/a/30012117/2303865 【参考方案1】:

我不确定您是否正在寻找某种 Objective-C 答案,但根据 RGBA 指定的颜色如何工作,我认为您可以根据任意因素简单地缩放 RGB 值以获得“较浅”或“较深”的阴影。例如,您可能有蓝色:

[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];

想要深蓝色?将 RGB 值乘以 0.9:

[UIColor colorWithRed:0.0 green:0.0 blue:0.9 alpha:1.0];

瞧。或者你有一个橙子:

[UIColor colorWithRed:1.0 green:0.4 blue:0.0 alpha:1.0];

选择另一个比例因子,例如 0.8:

[UIColor colorWithRed:0.8 green:0.32 blue:0.0 alpha:1.0];

这是您正在寻找的那种效果吗?

【讨论】:

好吧,这就是我需要的一半。有没有办法在蓝色为 1(最大)时获得更浅的颜色 @CoreCode 不,不是。除非您希望更改设备屏幕的亮度:) 请参阅我的回答。【参考方案2】:
- (UIColor *)lighterColorForColor:(UIColor *)c

    CGFloat r, g, b, a;
    if ([c getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MIN(r + 0.2, 1.0)
                               green:MIN(g + 0.2, 1.0)
                                blue:MIN(b + 0.2, 1.0)
                               alpha:a];
    return nil;


- (UIColor *)darkerColorForColor:(UIColor *)c

    CGFloat r, g, b, a;
    if ([c getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MAX(r - 0.2, 0.0)
                               green:MAX(g - 0.2, 0.0)
                                blue:MAX(b - 0.2, 0.0)
                               alpha:a];
    return nil;

像这样使用它:

UIColor *baseColor = // however you obtain your color
UIColor *lighterColor = [self lighterColorForColor:baseColor];
UIColor *darkerColor = [self darkerColorForColor:baseColor];

编辑:正如@Anchu Chimala 指出的那样,为了获得最大的灵活性,这些方法应该实现为 UIColor 类别。此外,根据@Riley 的想法,最好让颜色按比例变暗或变亮,而不是添加或减去常数值。正如@jrturton 指出的那样,没有必要操纵 RGB 组件;最好修改亮度属性本身。总而言之:

@implementation UIColor (LightAndDark)

- (UIColor *)lighterColor

    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:MIN(b * 1.3, 1.0)
                               alpha:a];
    return nil;


- (UIColor *)darkerColor

    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:b * 0.75
                               alpha:a];
    return nil;

@end

【讨论】:

当您可以对色调、饱和度、亮度做同样的事情时,为什么还要使用 RGB,然后只用改变亮度来重建颜色? 我自己一直在尝试使用 HSB 版本,但它没有按预期工作。尽管 UIKit 有getHue:saturation:brightness 签名,但它似乎适用于 HSV 而不是 HSB。在这种情况下更改亮度(实际上是值)将不起作用。例如,纯红色 (rgb(255,0,0)) 的亮度/值为 1,因此无法通过亮度进行照明。我最终改为更改 RGB 值。 我认为您的回答有些令人困惑:您采用两种方法(一种使用 RGB,另一种使用 HSB 用于类别版本),但结果不同.. . 这可能是一个意义问题:对我来说,变亮确实是使颜色趋向于白色(变暗为黑色),而改变亮度/值则使它变得更强(分别更亮)。 如果在灰色阴影上使用此方法将失败。 getHue:saturation:brightness:alpha 将返回 FALSE。 我想知道为什么这是公认的答案,因为两个建议的解决方案都是不正确。它们都只是改变了与人类感知为“较浅”或“较深”颜色无关的理论值。我建议阅读这篇很棒的帖子(短链接:goo.gl/qqgk9V)来理解我的意思。它解释了 LAB 颜色空间的 luminance 是您在使颜色更亮/更暗时应该关心的真正值。请参阅my answer 了解如何利用它并以正确的方式解决此问题。【参考方案3】:

如果将 RGB 颜色转换为 HSL color model,则可以将 L = 亮度分量从 L = 0.0(黑色)超过 L = 0.5(自然色)更改为 L = 1.0(白色)。 UIColor 不能直接处理 HSL,但是有转换 RGB HSL 的公式。

【讨论】:

不是 HSL,但 HSB 很好:developer.apple.com/library/ios/documentation/uikit/reference/…: HSL 和 HSB(有时也称为 HSV)颜色模型的区别在于 HSB L = 1.0 对应纯色,而 HSL L = 1.0 对应白色,L = 0.5到纯色。由于原始海报要求例如对于一种使蓝色 (RGB=0/0/1) 更亮的方法,我认为 HSL 更灵活。【参考方案4】:

TL;DR:

斯威夫特:

extension UIColor 

    var lighterColor: UIColor 
        return lighterColor(removeSaturation: 0.5, resultAlpha: -1)
    

    func lighterColor(removeSaturation val: CGFloat, resultAlpha alpha: CGFloat) -> UIColor 
        var h: CGFloat = 0, s: CGFloat = 0
        var b: CGFloat = 0, a: CGFloat = 0

        guard getHue(&h, saturation: &s, brightness: &b, alpha: &a)
            else return self

        return UIColor(hue: h,
                       saturation: max(s - val, 0.0),
                       brightness: b,
                       alpha: alpha == -1 ? a : alpha)
    

用法:

let lightColor = somethingDark.lighterColor

目标-C:

- (UIColor *)lighterColorRemoveSaturation:(CGFloat)removeS
                              resultAlpha:(CGFloat)alpha 
    CGFloat h,s,b,a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) 
        return [UIColor colorWithHue:h
                          saturation:MAX(s - removeS, 0.0)
                          brightness:b
                               alpha:alpha == -1? a:alpha];
    
    return nil;


- (UIColor *)lighterColor 
    return [self lighterColorRemoveSaturation:0.5
                                  resultAlpha:-1];

@rchampourlier 在他对@user529758 的评论中是正确的(接受的答案) - HSB(或 HSV)和 RGB 解决方案给出了完全不同的结果。 RGB 只是添加(或使颜色更接近)白色,而 HSB 解决方案使颜色更接近亮度等级的边缘 - 基本上以黑色开始并以纯色结束......

基本上亮度(值)使颜色更接近或更接近黑色,而饱和度使其更接近或更接近白色......

如下所示:

因此,使颜色实际上更亮(即更接近白色...)的解决方案是使其饱和度值更小,从而产生以下解决方案:

- (UIColor *)lighterColor 
    CGFloat h,s,b,a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) 
        return [UIColor colorWithHue:h
                          saturation:MAX(s - 0.3, 0.0)
                          brightness:b /*MIN(b * 1.3, 1.0)*/
                               alpha:a];
    
    return nil;

【讨论】:

关于使颜色更亮:通过降低饱和度和增加亮度/值,我得到了最好的结果。这个答案非常适合图像中饼图“顶部”的颜色。但是大多数颜色会落在馅饼“内部”的某个地方。使它们更接近白色也需要将它们“向上”移动(增加值)。 增加值可能看起来有效,但它不是“获得更浅的颜色”的“核心”功能。同样,要获得较深的颜色,正确的方法是仅减少“值”并保持饱和度不变...【参考方案5】:

如果您希望 user529758 的解决方案适用于灰色阴影(例如 [UIColor lightGrayColor][UIColor darkGrayColor],您必须像这样改进它:

- (UIColor *)lighterColor

    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) 
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:MIN(b * 1.3, 1.0)
                               alpha:a];
    

    CGFloat white, alpha;
    if ([self getWhite:&white alpha:&alpha]) 
        white = MIN(1.3*white, 1.0);
        return [UIColor colorWithWhite:white alpha:alpha];
    

    return nil;

getHue:saturation:brightness:alpha 在灰色阴影上调用时失败(并返回false),因此您需要使用getWhite:alpha

【讨论】:

【参考方案6】:

所有发布的解决方案相当都适用于所有颜色和阴影,但后来我偶然发现了this library,它为 UIColor 提供了一组非常好的实现扩展。

具体来说,它在其 HSL 实现中具有减轻功能:(UIColor *)lighten:(CGFloat)amount - 完美运行。

【讨论】:

【参考方案7】:

user529758 在 Swift 中的解决方案:

较深的颜色:

func darkerColorForColor(color: UIColor) -> UIColor 

       var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

       if color.getRed(&r, green: &g, blue: &b, alpha: &a)
           return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
       

       return UIColor()

浅色:

func lighterColorForColor(color: UIColor) -> UIColor 

       var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

       if color.getRed(&r, green: &g, blue: &b, alpha: &a)
           return UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
       

       return UIColor()

【讨论】:

这是一个很好的答案,但如果将它声明为 UIColor 类的扩展,例如 var darker: UIColor 会更方便。【参考方案8】:

UIColor 扩展和修复lighterColorForColor

extension UIColor 
  class func darkerColorForColor(color: UIColor) -> UIColor 
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if color.getRed(&r, green: &g, blue: &b, alpha: &a)
      return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
    
    return UIColor()
  

  class func lighterColorForColor(color: UIColor) -> UIColor 
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if color.getRed(&r, green: &g, blue: &b, alpha: &a)
      let tmpColor = UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
      println(tmpColor)
      return tmpColor
    
    return UIColor()
  

【讨论】:

【参考方案9】:

理想情况下,这些函数应该封装在一个名为 UIColor 的扩展名 UIColor+Brightness.swift 中,并且具有可配置的亮度 - 请参见下面的示例:

import UIKit

extension UIColor 

  func lighterColorWithBrightnessFactor(brightnessFactor:CGFloat) -> UIColor 
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if self.getRed(&r, green:&g, blue:&b, alpha:&a) 
      return UIColor(red:min(r + brightnessFactor, 1.0),
        green:min(g + brightnessFactor, 1.0),
        blue:min(b + brightnessFactor, 1.0),
        alpha:a)
    
    return UIColor()
  


【讨论】:

【参考方案10】:

Swift 通用扩展,适用于 iOS 和 OS X,使用 getHue

#if os(OSX)

    import Cocoa
    public  typealias PXColor = NSColor

    #else

    import UIKit
    public  typealias PXColor = UIColor

#endif

    extension PXColor 

    func lighter(amount : CGFloat = 0.25) -> PXColor 
        return hueColorWithBrightnessAmount(1 + amount)
    

    func darker(amount : CGFloat = 0.25) -> PXColor 
        return hueColorWithBrightnessAmount(1 - amount)
    

    private func hueColorWithBrightnessAmount(amount: CGFloat) -> PXColor 
        var hue         : CGFloat = 0
        var saturation  : CGFloat = 0
        var brightness  : CGFloat = 0
        var alpha       : CGFloat = 0

        #if os(iOS)

            if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) 
                return PXColor( hue: hue,
                                saturation: saturation,
                                brightness: brightness * amount,
                                alpha: alpha )
             else 
                return self
            

            #else

            getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
            return PXColor( hue: hue,
                            saturation: saturation,
                            brightness: brightness * amount,
                            alpha: alpha )

        #endif

    


用法:

let color = UIColor(red: 0.5, green: 0.8, blue: 0.8, alpha: 1.0)
color.lighter(amount:0.5)
color.darker(amount:0.5)

OR(使用默认值):

color.lighter()
color.darker()

示例:

【讨论】:

我收到一个错误:-getHue:saturation:brightness:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0 1;需要先转换色彩空间。 通过添加:let color = usingColorSpaceName(NSCalibratedRGBColorSpace) 然后将getHue 调用替换为颜色调用来修复它:color?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) 该函数在单词数量前缺少_。应该是private func hueColorWithBrightnessAmount(_ amount: CGFloat) -> PXColor 我建议将@Oskar 的建议添加到答案中。这确实为我解决了运行时崩溃问题。 亮度不应该等于1而不是0吗?截至var brightness : CGFloat = 1?【参考方案11】:

我根据状态值渲染彩色单元格:

为此,在使用 CryingHippo 的建议出现错误后,我基于一些旧的 objc 代码编写了一个快速扩展:

extension UIColor

    func darker(darker: CGFloat) -> UIColor

        var red: CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue: CGFloat = 0.0

        if self.colorSpace == UIColorSpace.genericGrayColorSpace()

            red =  whiteComponent - darker
            green = whiteComponent - darker
            blue  = whiteComponent - darker
         else 
            red = redComponent - darker
            green = greenComponent - darker
            blue = blueComponent - darker
        

        if red < 0
            green += red/2
            blue += red/2
        

        if green < 0
            red += green/2
            blue += green/2
        

        if blue < 0
            green += blue/2
            red += blue/2
        

        return UIColor(
            calibratedRed: red,
            green: green,
            blue: blue,
            alpha: alphaComponent
        )
    

    func lighter(lighter: CGFloat) -> UIColor
        return darker(-lighter)
    

NSColor 也一样。只需将UIColor 替换为NSColor

【讨论】:

【参考方案12】:

我只是想给出与 RGB 相同的结果

将带有 alpha x% 的颜色放在白色背景上以变亮 将带有 alpha x% 的颜色放在黑色背景上以变暗

AFAIK 给出的结果与在渐变大小的 x% 处选择渐变“颜色到白色”或“颜色到黑色”中的颜色相同。

为此,数学很简单:

extension UIColor 
    func mix(with color: UIColor, amount: CGFloat) -> UIColor 
        var red1: CGFloat = 0
        var green1: CGFloat = 0
        var blue1: CGFloat = 0
        var alpha1: CGFloat = 0

        var red2: CGFloat = 0
        var green2: CGFloat = 0
        var blue2: CGFloat = 0
        var alpha2: CGFloat = 0

        getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1)
        color.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2)

        return UIColor(
            red: red1 * (1.0 - amount) + red2 * amount,
            green: green1 * (1.0 - amount) + green2 * amount,
            blue: blue1 * (1.0 - amount) + blue2 * amount,
            alpha: alpha1
        )
    

以下是一些颜色的示例

【讨论】:

我认为这是最好的实现,无需借助实现 HSL 颜色空间的外部库【参考方案13】:

此线程中的所有其他答案都使用 RGB 颜色系统或简单地更改 HSB 系统的色调或亮度值。正如this great blog post 中详细解释的那样,使颜色变浅或变深的正确 方法是更改​​其luminance 值。其他答案都没有。如果您想做正确的事,请在阅读博文后使用我的解决方案自己编写


不幸的是,默认更改 UIColor 的任何属性非常麻烦。此外,Apple 甚至不支持任何基于 LAB 的色彩空间,例如 UIColor 类中的 HCL(LAB 中的 L 是我们正在寻找的 luminance 值)。

使用 HandyUIKit(通过 Carthage 安装)添加对 HCL 的支持,让您的生活轻松很多

import HandyUIKit    

let color = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)

// create a new UIColor object with a specific luminance (slightly lighter)
color.change(.luminance, to: 0.7)

还有一个选项可以应用相对更改(推荐):

// create a new UIColor object with slightly darker color
color.change(.luminance, by: -0.2)

请注意,HandyUIKit 还在您的项目中添加了其他一些方便的 UI 功能 - 查看其README on GitHub 了解更多详情。

希望对你有帮助!

免责声明:我是 HandyUIKit 的作者。

【讨论】:

对于我的应用程序,我在地图上绘制路线。我想在具有 100% 亮度的颜色的内线和该颜色的较暗变体的边界中绘制路线。因为内线已经有 100% 的亮度,所以这些方法都不起作用。像上面提到的那样改变亮度也没有奏效。然而,使用这个非常好的扩展来降低亮度对我来说是诀窍。 'color.change(.brightness, by: -0.5)' 这个扩展在使用上非常灵活并且有很多选项。我建议如果,特别是如果您有一个具有多个主题或调色板的应用程序。 终于有人懂颜色了。我知道每个人都只想在他们的代码中粘贴一点 UIColor 扩展,但你真的应该花一些时间来理解这个问题...... @fruitcoder 谢谢你,太慷慨了。我根本不是色彩专家,我只是阅读了链接的文章并试图为它提供一个简单的解决方案。很高兴,你喜欢它。【参考方案14】:

Sebyddd 解决方案作为扩展:

extension UIColor     
    func darker() -> UIColor 

        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if self.getRed(&r, green: &g, blue: &b, alpha: &a)
            return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
        

        return UIColor()
    

    func lighter() -> UIColor 

        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if self.getRed(&r, green: &g, blue: &b, alpha: &a)
            return UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
        

        return UIColor()
    

用法:

let darkerYellow = UIColor.yellow.darker()
let lighterYellow = UIColor.yellow.lighter()

【讨论】:

【参考方案15】:

这是一个 UIColor 类别,它还允许控制颜色变化量。

- (UIColor *)lighterColorWithDelta:(CGFloat)delta

    CGFloat r, g, b, a;
    if ([self getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MIN(r + delta, 1.0)
                               green:MIN(g + delta, 1.0)
                                blue:MIN(b + delta, 1.0)
                               alpha:a];
    return nil;


- (UIColor *)darkerColorWithDelta:(CGFloat)delta

    CGFloat r, g, b, a;
    if ([self getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MAX(r - delta, 0.0)
                               green:MAX(g - delta, 0.0)
                                blue:MAX(b - delta, 0.0)
                               alpha:a];
    return nil;

【讨论】:

【参考方案16】:

基于@Sebyddd 答案的 Swift 扩展:

import Foundation
import UIKit

extension UIColor
    func colorWith(brightness: CGFloat) -> UIColor
        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if getRed(&r, green: &g, blue: &b, alpha: &a)
            return UIColor(red: max(r + brightness, 0.0), green: max(g + brightness, 0.0), blue: max(b + brightness, 0.0), alpha: a)
        

        return UIColor()
    

【讨论】:

【参考方案17】:

在 Xcode 10 中使用适用于 iOS 12 的 Swift 4.x 进行测试

从作为 UIColor 的颜色开始,然后选择一个变暗因子(作为 CGFloat)

let baseColor = UIColor.red
let darkenFactor: CGFloat = 2

CGColor 类型有一个可选值components,它将颜色分解为 RGBA(作为值介于 0 和 1 之间的 CGFloat 数组)。然后,您可以使用从 CGColor 获取的 RGBA 值重构 UIColor 并对其进行操作。

let darkenedBase = UIColor(displayP3Red: startColor.cgColor.components![0] / darkenFactor, green: startColor.cgColor.components![1] / darkenFactor, blue: startColor.cgColor.components![2] / darkenFactor, alpha: 1)

在此示例中,每个 RGB 值都除以 2,使颜色变为以前的一半。 alpha 值保持不变,但您也可以在 alpha 值而不是 RGB 上应用变暗因子。

【讨论】:

【参考方案18】:

对于较深的颜色,这是最简单的: theColor = [theColor shadowWithLevel:s]; //s:0.0 到 1.0

【讨论】:

此方法仅适用于 NSColor,不适用于 UIColor。【参考方案19】:

斯威夫特 5

extension UIColor 

func lighter(by percentage:CGFloat=30.0) -> UIColor? 
    return self.adjust(by: abs(percentage) )


func darker(by percentage:CGFloat=30.0) -> UIColor? 
    return self.adjust(by: -1 * abs(percentage) )


func adjust(by percentage:CGFloat=30.0) -> UIColor? 
    var r:CGFloat=0, g:CGFloat=0, b:CGFloat=0, a:CGFloat=0;
    if self.getRed(&r, green: &g, blue: &b, alpha: &a) 
        return UIColor(red: min(r + percentage/100, 1.0),
                       green: min(g + percentage/100, 1.0),
                       blue: min(b + percentage/100, 1.0),
                       alpha: a)
     else 
        return nil
     
   

【讨论】:

以上是关于从 UIColor 获得稍微更亮和更暗的颜色的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript获取某个颜色更亮或更暗的颜色

将检测到的文本设置为比图像更暗 - ccv

如何使任何悬停的颜色比原始颜色更暗/更亮

MATLAB 中的 Gamma 校正实现

如何确定给定颜色的较深或较浅颜色变体?

利用伪元素单个颜色实现 hover 和 active 时的明暗变化效果