从UIColor获取稍微浅一些和稍微深一些的颜色

151

我希望能够将任何UIColor转换为渐变色。我的想法是使用Core Graphics来绘制一个渐变。我想做的是获取一种颜色,比如:

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

如何获取一个颜色比原来深或浅几个阴影。有谁知道怎么做吗?谢谢。


1
“渐变”意味着图像的一部分将成为某种颜色的阴影,而另一部分将成为更暗或更浅的阴影。这是您想要做的另一种定义方式吗? - Michael Dautermann
2
看一下这个库 https://github.com/yannickl/DynamicColor - onmyway133
Swift使用色相、饱和度和亮度 - Leo Dabus
19个回答

284
- (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

2
为什么要使用RGB,当你可以用色调、饱和度、亮度来实现同样的效果,然后只需改变亮度重新构建颜色呢? - jrturton
3
我自己尝试了HSB版本,但效果不如预期。尽管UIKit有getHue:saturation:brightness这个方法,但它似乎使用HSV而不是HSB进行计算。在这种情况下改变亮度(实际上是值)将无效。例如,纯红色(rgb(255,0,0))的亮度/值为1,因此通过改变亮度调节亮度是不可能的。最终我选择对RGB值进行修改来解决问题。 - Romain Champourlier
2
我认为你的回答有些混淆:你采用了两种不同的方法(一种是RGB,另一种是HSB用于类别版本),它们的结果不同...这可能是一个意义上的问题:对我而言,亮化确实是让颜色趋近于白色(变暗则是黑色),而改变亮度/值则是让其更强烈(或更轻)。 - Romain Champourlier
4
如果这些方法用于灰度图像,它们将失败。getHue:saturation:brightness:alpha会返回错误的结果。 - Matthieu Riegler
2
我想知道为什么这是“被接受的答案”,因为两个建议的解决方案都是错误的。它们只是改变了理论值,而这些值与人类感知的“浅色”或“深色”颜色几乎没有关系。我建议阅读这篇优秀的文章(短链接:goo.gl/qqgk9V),以了解我的意思。它解释了LAB颜色空间的亮度值是您在使颜色变浅/变暗时应关注的真正价值。请参见我的答案,了解如何利用它并以正确的方式解决此问题。 - Jeehut
显示剩余8条评论

61

简述:

Swift:

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

Objective-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解决方案将颜色更接近亮度比例尺上的边缘 - 基本上从黑色开始直到纯色...

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

如下图所示:

HSV color graph

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

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

关于让颜色变浅的问题:我通过降低饱和度和增加亮度/数值获得了最好的结果。这个答案对于图像中“在饼图顶部”的颜色非常有效。但是大多数颜色将会位于饼图的“内部”。要使它们更接近白色,还需要将它们“上移”(增加数值)。 - pejalo
增加值看起来可能会起作用,但这不会是“获得较浅颜色”的“核心”功能。同样地,要获得较深的颜色,正确的方法是仅减少“价值”,并保持饱和度不变... - Aviel Gross

42

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)

或者(使用默认值):

color.lighter()
color.darker()

示例:

在此输入图片描述


我遇到了一个错误:-getHue:saturation:brightness:alpha: 对于 NSColor NSCalibratedWhiteColorSpace 0 1 是无效的;需要先转换颜色空间。 - Besi
2
通过添加以下代码进行修复:let color = usingColorSpaceName(NSCalibratedRGBColorSpace)然后将 getHue 调用替换为对 color 的调用: color?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) - Oskar
该函数在单词“amount”之前缺少一个下划线“_”。应为private func hueColorWithBrightnessAmount(_ amount: CGFloat) -> PXColor { - Codemonk
我建议将@Oskar的建议添加到答案中。这确实为我修复了一个运行时崩溃。 - Besi
亮度不应该等于0而是等于1吗?因为变量var brightness : CGFloat = 1的值是1 - denis_lor
代码中有一个错误。在调用hueColorWithBrightnessAmount的lighter和darker方法时缺少参数。 - Sentry.co

37

我只想提供与以下相同的RGB结果:

  • 将该颜色以 alpha x% 的透明度放置在白色背景上以变亮
  • 将该颜色以 alpha x% 的透明度放置在黑色背景上以变暗

根据我所知,这样做会得到与在“颜色到白色”或“颜色到黑色”的渐变中以 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
        )
    }
}

这里是带有一些颜色的示例

较深和较浅的示例


1
我认为这是最好的实现方式,而不需要使用实现HSL颜色空间的外部库。 - gog

24

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

1
这是一个很好的答案,只不过如果它被声明为UIColor类的扩展,例如var darker: UIColor,那么它会更方便。 - Bruno Philipe

14

如果将RGB颜色转换为HSL颜色模型,则可以通过变化L = 亮度分量从L = 0.0(黑色)到L = 0.5(自然颜色)再到L = 1.0(白色)。UIColor无法直接处理HSL,但有用于转换RGB <-> HSL的公式。


不是 HSL,而是 HSB 就可以了:http://developer.apple.com/library/ios/documentation/uikit/reference/UIColor_Class/Reference/Reference.html#//apple_ref/occ/instm/UIColor/getHue:saturation:brightness:alpha: - jrturton
9
HSL和HSB(有时也称为HSV)颜色模型之间的区别在于,在HSB中,L = 1.0对应于纯色,而在HSL中,L = 1.0对应于白色,L = 0.5对应于纯色。由于原始帖子询问如何使蓝色(RGB = 0/0/1)变浅,因此我认为HSL更具灵活性。 - Martin R

8

这个线程中的所有其他答案都使用 RGB 颜色系统 或者仅仅是改变 HSB 系统的色相或亮度值。正如 这篇优秀的博客文章 中详细解释的那样,使颜色变浅或变暗的正确方法是改变其 亮度 值。其他答案都没有这样做。如果你想做得正确,那么就 使用我的解决方案 或者在阅读了博客文章后自己编写


很不幸,默认情况下更改UIColor的任何属性都是相当麻烦的。此外,苹果甚至不支持任何基于LAB的颜色空间,例如HCL在UIColor类中(LAB中的L是我们正在寻找的亮度值)。
使用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功能添加到您的项目中 - 请查看GitHub上的README以获取更多详细信息。
希望这有所帮助!
免责声明:我是HandyUIKit的作者。

对于我的应用程序,我在地图上绘制路线。我想用一种颜色的内部线条绘制路线,亮度为100%,边框为该颜色的较暗变体。因为内部线条已经有了100%的亮度,所以没有任何方法起作用。像上面提到的更改亮度也没有起作用。然而,使用这个非常好的扩展来降低亮度对我很有帮助。'color.change(.brightness, by: -0.5)' 这个扩展在使用上非常灵活,并且有很多选项。如果您的应用程序具有多个主题或颜色调色板,我建议使用它。 - guido
终于有人理解颜色了。我知道每个人都只想在他们的代码中粘贴一个小的UIColor扩展,但你真的应该花些时间去理解这个问题... - fruitcoder
@fruitcoder 谢谢,你太慷慨了。我并不是一个色彩专家,只是阅读了相关文章,并尝试提供了一个简单的解决方案。很高兴你喜欢它。 - Jeehut

6

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

6

所有已经发布的解决方案都不能完全适用于所有颜色和色调,但是我偶然发现了这个库,它提供了一组非常好的UIColor扩展。

具体来说,它在HSL实现中有一个lighten函数:(UIColor *)lighten:(CGFloat)amount - 它完美地解决了问题。


5

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接