ChatGPT解决这个技术问题 Extra ChatGPT

Get Slightly Lighter and Darker Color from UIColor

I was looking to be able to turn any UIColor into a gradient. The way I am intending to do this is by using Core Graphics to draw a gradient. What I am trying to do is to get a color, lets say:

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

and get a UIColor which is a few shades darker and a few shades lighter. Does anyone know how to do this? Thank you.

"gradient" implies that one portion of your image will be one shade of the color while another portion will be a darker or lighter shade. Is this another way of defining what you want to do?
Take a look at this library github.com/yannickl/DynamicColor
Swift using hue saturation and lightness stackoverflow.com/a/30012117/2303865

L
Lukas Kubanek
- (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;
}

Use it like this:

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

EDIT: as @Anchu Chimala pointed out, for maximum flexibility, these methods should be implemented as an UIColor category. Also, from @Riley's idea, it may be a better idea to make the color proprtionally darker or lighter instead of adding or subtracting constant values. As @jrturton pointed out, it's not necessary to manipulate the RGB components; it's better to modify the brightness property itself. All in all:

@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

Why use RGB when you can do the same with hue,saturation, brightness, then just rebuild the colour with the brightness changed?
I've been trying with the HSB version myself, and it didn't work as expected. Despite the getHue:saturation:brightness signature of UIKit, it seems it works with HSV instead of HSB. Changing the brightness (so in fact, the value) in this context will not work. For example pure Red (rgb(255,0,0)) will have a brightness/value of 1, thus making lightening through the brightness impossible. I ended up working with changes on RGB values instead.
I think that there is something confusing in your answer: you take the two approaches (one with RGB, and the other with HSB for the category version), which don't have the same result... And that may be a question of meaning: lightening is indeed for me making the color tend to the white (darkening to the black), while changing the brightness/value is making it stronger (resp. lighter).
This methods will fail if they are used on gray shades. getHue:saturation:brightness:alpha will return FALSE.
I wonder why this is the accepted answer as both of the suggested solutions are incorrect. They both merely change theoretical values that have little to do with what human beings perceive as 'lighter' or 'darker' colors. I recommend reading this great post (shortlink: goo.gl/qqgk9V) to understand what I mean. It explains that the luminance value of the LAB color space is the true one you should care about when making colors lighter/darker. See my answer on how you can make use of it and solve this problem the right way.
A
Aviel Gross

TL;DR:

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

Usage:

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 was right in his comment to @user529758 (The accepted answer) - The HSB (Or HSV) and RGB solutions give completely different results. RGB just adds (Or makes the color closer to) white, and the HSB solution brings the color closer to the edge in the Brigtness scale - which basically start with black and ends with the pure color...

Basically Brightness (Value) makes the color less or more closer to black, where Saturation makes it less or more closer to white...

As seen here:

https://i.stack.imgur.com/LjbFa.png

So the solution to make a color actually brighter (i.e. closer to white...) will be to make it's Saturation value smaller, resulting this solution:

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

Regarding making colors lighter: I got the best result by reducing saturation AND increasing brightness/value. This answer works great for colors "on top" of the pie in the image. But most colors are going to fall somewhere "inside" the pie. Bringing them closer to white requires moving them "up" as well (increasing value).
Increasing value might appear to work, but it won't be the "core" functionality of "get a lighter color". The same way, to get a darker color, the right approach would be to only decrease "Value" and leave the Saturation the way it is...
C
CryingHippo

Swift universal extension for iOS and OS X, using 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

    }

}

Usage :

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 (with the default values):

color.lighter()
color.darker()

Sample :

https://i.stack.imgur.com/ufMd6.png


I got an error: -getHue:saturation:brightness:alpha: not valid for the NSColor NSCalibratedWhiteColorSpace 0 1; need to first convert colorspace.
Fix it by adding: let color = usingColorSpaceName(NSCalibratedRGBColorSpace) and then replacing the getHue call with a call to color: color?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
The function is missing a _ before the word amount. Should be private func hueColorWithBrightnessAmount(_ amount: CGFloat) -> PXColor {
I suggest to add @Oskar 's suggestion to the answer. This did fix a runtime crash for me.
Shouldn't brightness be equal to 1 instead of 0? As of var brightness : CGFloat = 1?
R
Rudolf Adamkovič

I just wanted to give the same result, in RGB, than

placing the color with alpha x% over a white background to lighten

placing the color with alpha x% over a black background to darken

Which gives the same result, AFAIK, than picking the color in a gradient 'color to white' or 'color to black', at x% of the gradient size.

For that purpose, the math is simple:

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

Here are examples with some colors

https://i.stack.imgur.com/KZr5U.png


I think this is the best implementation without resorting to an external library that implements the HSL color space
J
Justin Levi Winter

user529758's solution in Swift:

Darker color:

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

Lighter color:

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

This is a good answer, except it would be more convenient were it declared as an extension to the UIColor class simply as var darker: UIColor for example.
M
Martin R

If you convert the RGB color to the HSL color model then you can vary the L = lightness component from L = 0.0 (black) over L = 0.5 (natural color) to L = 1.0 (white) . UIColor cannot handle HSL directly, but there are formula for converting RGB <-> HSL.


The difference between the HSL and the HSB (sometimes also called HSV) color model is that in HSB L = 1.0 corresponds to the pure color, whereas in HSL L = 1.0 corresponds to white and L = 0.5 to the pure color. Since the original poster asked e.g. for a way to make the color blue (RGB=0/0/1) lighter, I think that HSL is more flexible.
K
Koen.

All other answers in this thread use either the RGB color system or simply change the hue or brightness value of the HSB system. As explained in detail in this great blog post the correct way of making a color lighter or darker is to change its luminance value. None of the other answers does that. If you want to do it right, then use my solution or write your own after reading the blog post.

Unfortunately it's quite a hassle to change any of the attributes of a UIColor by default. Also Apple doesn't even support any LAB-based color space like HCL in the UIColor class (the L in LAB is the luminance value we are looking for).

Using HandyUIKit (install it via Carthage) adds support for HCL and makes your life a lot easier:

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)

There is also an option to apply a relative change (recommended):

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

Note that HandyUIKit also adds some other handy UI features into your project – checkout its README on GitHub for more details.

I hope it helps!

Disclaimer: I'm the author of HandyUIKit.


For my app I draw routes on maps. I wanted to draw the route with in inside line of a color with 100% brightness and the border with a darker variant of that colour. Because the inside line already had 100% brightness none of the methods worked. Also changing the luminance like mentioned above did not do the trick. However, reducing the brightness using this very nice extension did the trick for me. 'color.change(.brightness, by: -0.5)' This extension is very flexible in it's use and has lots of options. I recommend if, especially if you have an app with several themes or color palettes.
FINALLY someone who understands colors. I know everyone just wants to paste a little UIColor extension in their code but you should really invest some time to understand the problem...
@fruitcoder Thank you, too generous. I'm not a color expert at all, I just read the linked article and tried to provide a simple solution for it. Glad, you like it.
R
RyanM

None of the solutions posted quite worked for all colours and shades, but then I stumbled across this library which provides a set of very well implemented extensions to UIColor.

Specifically it has a lighten function as part of its HSL implementation: (UIColor *)lighten:(CGFloat)amount - which works perfectly.


H
Hemang

Sebyddd solution as an extension:

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

Usage:

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

M
Matthieu Riegler

If you want user529758's solution to work with gray shades (like [UIColor lightGrayColor] or [UIColor darkGrayColor] you have to improve it like that:

- (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 fails (and returns false) when called on a gray shade therefore you'll need to use getWhite:alpha.


A
Ahmadreza

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

J
Justin Levi Winter

UIColor extension and fixing 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()
  }
}

r
ravron

I'm not sure if you're looking for some sort of Objective-C answer, but based on how colors specified by RGBA work, I think you can simply scale the RGB values according to an arbitrary factor to get a "lighter" or "darker" shade. For example, you might have a blue:

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

Want a darker blue? Multiply the RGB values by 0.9:

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

Voila. Or maybe you have an orange:

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

Choose another scale factor, say, 0.8:

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

Is that the sort of effect you're looking for?


Ok Yeah that is half of what I need. Is there a way to get a lighter color then when blue is 1 (Max)
@CoreCode no, not really. Unless you wish to change the birghtness of the device's screen :) See my answer.
M
Microbob

Tested in Xcode 10 with Swift 4.x for iOS 12

Start with your color as a UIColor and pick a darkening factor (as a CGFloat)

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

The type CGColor has an optional value components which break down the color into RGBA (as a CGFloat array with values between 0 and 1). You can then reconstruct a UIColor using RGBA values taken from the CGColor and manipulate them.

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

In this example, each of the RGB valuse were divided by 2, making the color half as dark as it was before. The alpha value remained the same, but you could alternatively apply the darken factor on the alpha value rather than the RGB.


Z
Zorayr

Ideally, the functions should be encapsulated inside a UIColor extension called, UIColor+Brightness.swift, and have a configurable brightness - see example below:

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

}

B
Besi

I render coloured cells based on a status value:

https://i.stack.imgur.com/NUXNW.png

For this I wrote a swift extension based on some old objc code after I got an error using CryingHippo's suggestion:

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

The same works for NSColor as well. Simply replace UIColor with NSColor.


B
Blago

Here is a UIColor category that also allows control over the amount of color change.

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

B
Balázs Vincze

A Swift extension based on @Sebyddd answer:

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

J
Jiulong Zhao

for darker color, this is the simplest: theColor = [theColor shadowWithLevel:s]; //s:0.0 to 1.0


This method is only available on NSColor, not UIColor.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now