ChatGPT解决这个技术问题 Extra ChatGPT

Get lighter and darker color variations for a given UIColor

How to get different lighter and darker variations of a given UIColor in Swift?

https://i.stack.imgur.com/zVrrf.jpg

title question is pretty trivial and allow different solutions; also you can use one function instead two using negative input parameter
Various techniques were proposed here stackoverflow.com/questions/11598043/…, there are also answers in Swift.
@ShadowOf updated, thanks for input
Swift - 4.0 Click hare for answer
this answer was already available at the time you posted your question. In general, I think this question and the other are very similar, if not duplicated.

S
Stephen

Updated

Use below UIColor Extension:

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 red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
        if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
            return UIColor(red: min(red + percentage/100, 1.0),
                           green: min(green + percentage/100, 1.0),
                           blue: min(blue + percentage/100, 1.0),
                           alpha: alpha)
        } else {
            return nil
        }
    }
}

Usage:

let color = UIColor(red:0.96, green:0.54, blue:0.10, alpha:1.0)
color.lighter(30) // returns lighter color by 30%
color.darker(30) // returns darker color by 30%

instead of .lighter() and .darker(), you can use .adjust() with positive values for lightening and negative values for darkening

color.adjust(-30) // 30% darker color
color.adjust(30) // 30% lighter color

Output:

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


You modify all components by the same value instead of changing them proportionally. See how orange becomes yellow when you make it lighter.
Adding values proportionally isn't correct. try this color UIColor(red:0.89, green:0.28, blue:0.00, alpha:1.0)
But adding the same value to all components isn't correct either, it changes the color. Have a look at the various answers to stackoverflow.com/questions/11598043/… where the brightness in the HSB representation is adjusted.
@MartinR I have executed your function and mine, both outputs are same(orange did not become yellow ;-) ). However i need to improve skill to reduce the code. i should have avoided converting to RGB values and write logic in single function. will update the code
I believe for black color lighter , returns green color
a
aheze

I want to provide another version using brightness & saturation instead of RGB

extension UIColor {
  /**
   Create a lighter color
   */
  func lighter(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: abs(percentage))
  }
  
  /**
   Create a darker color
   */
  func darker(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: -abs(percentage))
  }
  
  /**
   Try to increase brightness or decrease saturation
   */
  func adjustBrightness(by percentage: CGFloat = 30.0) -> UIColor {
    var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
    if self.getHue(&h, saturation: &s, brightness: &b, alpha: &a) {
      if b < 1.0 {
        let newB: CGFloat = max(min(b + (percentage/100.0)*b, 1.0), 0.0)
        return UIColor(hue: h, saturation: s, brightness: newB, alpha: a)
      } else {
        let newS: CGFloat = min(max(s - (percentage/100.0)*s, 0.0), 1.0)
        return UIColor(hue: h, saturation: newS, brightness: b, alpha: a)
      }
    }
    return self
  }
}

I think this is better than modifying the RGB values
Don't think this works when saturation is 0 and brightness is 1.0. IE white. In this case you end up with unchanged saturation.
(You cannot brighten white.) This is the correct approach. The RGB answers are totally incorrect on many levels.
But you can darken white (it should turn gray) but this does not do that :/
M
Mojtaba Hosseini

SwiftUI: Color - iOS 14 / macOS 10.16

extension Color {
    public func lighter(by amount: CGFloat = 0.2) -> Self { Self(UIColor(self).lighter(by: amount)) }
    public func darker(by amount: CGFloat = 0.2) -> Self { Self(UIColor(self).darker(by: amount)) }
}

Reqires one of the followings (or both) for iOS or (and) macOS

AppKit: NSColor

extension NSColor {
    func mix(with color: NSColor, amount: CGFloat) -> Self {
        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 Self(
            red: red1 * CGFloat(1.0 - amount) + red2 * amount,
            green: green1 * CGFloat(1.0 - amount) + green2 * amount,
            blue: blue1 * CGFloat(1.0 - amount) + blue2 * amount,
            alpha: alpha1
        )
    }

    func lighter(by amount: CGFloat = 0.2) -> Self { mix(with: .white, amount: amount) }
    func darker(by amount: CGFloat = 0.2) -> Self { mix(with: .black, amount: amount) }
}

UIKit: UIColor

extension UIColor {
    func mix(with color: UIColor, amount: CGFloat) -> Self {
        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 Self(
            red: red1 * CGFloat(1.0 - amount) + red2 * amount,
            green: green1 * CGFloat(1.0 - amount) + green2 * amount,
            blue: blue1 * CGFloat(1.0 - amount) + blue2 * amount,
            alpha: alpha1
        )
    }

    func lighter(by amount: CGFloat = 0.2) -> Self { mix(with: .white, amount: amount) }
    func darker(by amount: CGFloat = 0.2) -> Self { mix(with: .black, amount: amount) }
}

F
Fattie

To save anyone typing, the simple practical version is just

extension UIColor {

    var darker: UIColor {

    var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0

        guard self.getHue(&h, saturation: &s, brightness: &b, alpha: &a) else {
            print("** some problem demuxing the color")
            return .gray
        }

        let nudged = b * 0.5

        return UIColor(hue: h, saturation: s, brightness: nudged, alpha: a)
    }
}

use like

something.color = .yellow.darker

or

backgroundColor = backgroundColor.darker

On a large project .................

You should definitely extend Apple's pattern:

.withAlphaComponent(_ alpha: CGFloat)

So, have:

.withBrightnessComponent(_ alpha: CGFloat)

and distinctly

.withBrightnessComponentAdjustedBy(percentage: CGFloat)

and/or

.withBrightnessComponentMultipliedBy(factor: CGFloat)

H
Hikeland

Since I use SwiftUI in my current project, I adapted the best answer from Stephen. Tested with Xcode 12.0, SwiftUI 2 and iOS 14.0

extension Color {
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, opacity: CGFloat) {
        #if canImport(UIKit)
        typealias NativeColor = UIColor
        #elseif canImport(AppKit)
        typealias NativeColor = NSColor
        #endif

        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var o: CGFloat = 0

        guard NativeColor(self).getRed(&r, green: &g, blue: &b, alpha: &o) else {
            return (0, 0, 0, 0)
        }
        return (r, g, b, o)
    }
    
    func lighter(by percentage: CGFloat = 30.0) -> Color {
        return self.adjust(by: abs(percentage) )
    }

    func darker(by percentage: CGFloat = 30.0) -> Color {
        return self.adjust(by: -1 * abs(percentage) )
    }

    func adjust(by percentage: CGFloat = 30.0) -> Color {
        return Color(red: min(Double(self.components.red + percentage/100), 1.0),
                     green: min(Double(self.components.green + percentage/100), 1.0),
                     blue: min(Double(self.components.blue + percentage/100), 1.0),
                     opacity: Double(self.components.opacity))
    }
}

B
BSK-Team

For Swift 5.0 :

extension UIColor {

func lighter(by percentage: CGFloat = 10.0) -> UIColor {
    return self.adjust(by: abs(percentage))
}

func darker(by percentage: CGFloat = 10.0) -> UIColor {
    return self.adjust(by: -abs(percentage))
}

func adjust(by percentage: CGFloat) -> UIColor {
    var alpha, hue, saturation, brightness, red, green, blue, white : CGFloat
    (alpha, hue, saturation, brightness, red, green, blue, white) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

    let multiplier = percentage / 100.0

    if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
        let newBrightness: CGFloat = max(min(brightness + multiplier*brightness, 1.0), 0.0)
        return UIColor(hue: hue, saturation: saturation, brightness: newBrightness, alpha: alpha)
    }
    else if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
        let newRed: CGFloat = min(max(red + multiplier*red, 0.0), 1.0)
        let newGreen: CGFloat = min(max(green + multiplier*green, 0.0), 1.0)
        let newBlue: CGFloat = min(max(blue + multiplier*blue, 0.0), 1.0)
        return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)
    }
    else if self.getWhite(&white, alpha: &alpha) {
        let newWhite: CGFloat = (white + multiplier*white)
        return UIColor(white: newWhite, alpha: alpha)
    }

    return self
    }
}

C
ChrisDL

Kenji-Tran's answer works fine, as long as your starting color is not black (brightness value 0). With the addition of a few lines of extra code, you can also make black "lighter" (i.e. brighten it to a grayscale or color value).

Note: I wasn't able to add this change using an Edit and I'm not allowed to comment on Kenji-Tran's answer due to my "new boy" rep, therefore I found no other way to share my knowledge on SO then by posting a new answer. I hope that's okay.

extension UIColor {
  /**
   Create a ligher color
   */
  func lighter(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: abs(percentage))
  }

  /**
   Create a darker color
   */
  func darker(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: -abs(percentage))
  }

  /**
   Try to increase brightness or decrease saturation
   */
  func adjustBrightness(by percentage: CGFloat = 30.0) -> UIColor {
    var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
    if self.getHue(&h, saturation: &s, brightness: &b, alpha: &a) {
      if b < 1.0 {
        /**
         Below is the new part, which makes the code work with black as well as colors
        */
        let newB: CGFloat
        if b == 0.0 {
            newB = max(min(b + percentage/100, 1.0), 0.0)
        } else {
            newB = max(min(b + (percentage/100.0)*b, 1.0), 0,0)
        }
        return UIColor(hue: h, saturation: s, brightness: newB, alpha: a)
      } else {
        let newS: CGFloat = min(max(s - (percentage/100.0)*s, 0.0), 1.0)
        return UIColor(hue: h, saturation: newS, brightness: b, alpha: a)
      }
    }
    return self
  }
}

Thanks for pointing out the missing case (y). May I ask a question? Why it is b == 0.0 && s == 0.0? I thought just want more case b == 0 is enough to solve the issue. Because if b is 0 and s > 0, your code still not be able to make the color ligher?
Good question. It's a mistake. I have fixed the code. The reason I checked if s == 0 was because I wanted it to work for black to grayscale. But obviously, without the s == 0 it works for any color with brightness zero. Thank you.
l
lukszar

Version with RGB values modification

Here I put simple UIColor extension which is based on previous answers. It's working perfectly for me.

Below demo:

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

Colors manipulation code

public extension UIColor {

    /**
     Create a lighter color
     */
    public func lighter(by percentage: CGFloat = 30.0) -> UIColor {
        return self.adjustBrightness(by: abs(percentage))
    }

    /**
     Create a darker color
     */
    public func darker(by percentage: CGFloat = 30.0) -> UIColor {
        return self.adjustBrightness(by: -abs(percentage))
    }

    /**
     Changing R, G, B values
     */

    func adjustBrightness(by percentage: CGFloat = 30.0) -> UIColor {

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

        if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {

            let pFactor = (100.0 + percentage) / 100.0

            let newRed = (red*pFactor).clamped(to: 0.0 ... 1.0)
            let newGreen = (green*pFactor).clamped(to: 0.0 ... 1.0)
            let newBlue = (blue*pFactor).clamped(to: 0.0 ... 1.0)

            return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)
        }

        return self
    }
}

Clamped function Extension to easily keep values between min and max.

extension Comparable {

    func clamped(to range: ClosedRange<Self>) -> Self {

        if self > range.upperBound {
            return range.upperBound
        } else if self < range.lowerBound {
            return range.lowerBound
        } else {
            return self
        }
    }
}

W
Warpling

Swift 4 version that supports RGBA, HSBA, and WB (greyscale)

Here's a variation of TranQuan's answer that also supports greyscale colors like .white and .black. (Note: I removed saturation adjustment because I didn't think it belonged in a simple function like this.)

extension UIColor {
    /**
     Create a ligher color
     */
    func lighter(by percentage: CGFloat = 10.0) -> UIColor {
        return self.adjustBrightness(by: abs(percentage))
    }

    /**
     Create a darker color
     */
    func darker(by percentage: CGFloat = 10.0) -> UIColor {
        return self.adjustBrightness(by: -abs(percentage))
    }

    /**
     Try to adjust brightness and falls back to adjusting colors if necessary
     */
    func adjustBrightness(by percentage: CGFloat) -> UIColor {
        var alpha, hue, saturation, brightness, red, green, blue, white : CGFloat
        (alpha, hue, saturation, brightness, red, green, blue, white) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

        let multiplier = percentage / 100.0

        if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
            let newBrightness: CGFloat = max(min(brightness + multiplier*brightness, 1.0), 0.0)
            return UIColor(hue: hue, saturation: saturation, brightness: newBrightness, alpha: alpha)
        }
        else if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
            let newRed: CGFloat = min(max(red + multiplier*red, 0.0), 1.0)
            let newGreen: CGFloat = min(max(green + multiplier*green, 0.0), 1.0)
            let newBlue: CGFloat = min(max(blue + multiplier*blue, 0.0), 1.0)
            return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)
        }
        else if self.getWhite(&white, alpha: &alpha) {
            let newWhite: CGFloat = (white + multiplier*white)
            return UIColor(white: newWhite, alpha: alpha)
        }

        return self
    }
}

This works great, thank you!
I actually can't seem to brighten .black or really really dark gray, so as a workaround I created another function that takes a percentage and sets the color's brightness directly to that level. The issue is the formula: brightness + multiplier * brightness, this doesn't work when the brightness is 0 or really small. So instead you can create a color like this return UIColor(hue: hue, saturation: saturation, brightness: max(min(percentage / 100.0, 1.0), 0.0), alpha: alpha)
O
Oscar

Using lukszar clampled function, I wrote this function for the UIColor extension, using real proportions of RGB values. I hope it is helpful

public extension UIColor {

  public func lighter(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: abs(percentage))
  }

  public func darker(by percentage: CGFloat = 30.0) -> UIColor {
    return self.adjustBrightness(by: -abs(percentage))
  }

  func adjustBrightness(by percentage: CGFloat = 30.0) -> UIColor {

    let ratio = percentage/100

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

    if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
      let newRed =   (red   + ((ratio < 0) ? red   * ratio : (1 - red)   * ratio)).clamped(to: 0.0 ... 1.0)
      let newGreen = (green + ((ratio < 0) ? green * ratio : (1 - green) * ratio)).clamped(to: 0.0 ... 1.0)
      let newBlue =  (blue  + ((ratio < 0) ? blue  * ratio : (1 - blue)  * ratio)).clamped(to: 0.0 ... 1.0)
      return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)
    }
    return self
  }
}

M
Mr.Javed Multani

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

The code example below demonstrate how you can get a lighter and darker shade of a given color, useful in applications having dynamic themes

For Darker Color

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

For Lighter Color

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


P
Pavel Lobodinský

For macOS apps, there is an in-built function for color blending.

To make a color lighter, simply call

NSColor.systemRed.blended(withFraction: 0.35, of: .white)

a
atultw

I'm using SwiftUI and was looking for a quick solution.

This method changes the alpha channel (0 is transparent, 1 is opaque) and puts it in front of a white color view, so you're actually mixing white with a color. Higher alpha value, more white mixed in = brighter.

Converting the Color to UIColor, modifying, and converting back does the job:

Color(UIColor(Color.blue).withAlphaComponent(0.5))
    .background(Color.white) // IMPORTANT: otherwise your view will be see-through

To darken a color change Color.white to Color.black