Reputation: 3465
How to get different lighter and darker variations of a given UIColor in Swift?
Upvotes: 94
Views: 38972
Reputation: 119302
With this method, you can mix colors in any platform with any color. In this case black
for darker and white
for lighter appearance:
// MARK: - Cross-Platform Color Alias
#if canImport(UIKit)
public typealias NativeColor = UIColor
#elseif canImport(AppKit)
public typealias NativeColor = NSColor
#endif
// MARK: - Native Multiplatform Color Mixer
public extension NativeColor {
func mix(with target: NativeColor, amount: CGFloat) -> Self {
var r1: CGFloat = 0, g1: CGFloat = 0, b1: CGFloat = 0, a1: CGFloat = 0
var r2: CGFloat = 0, g2: CGFloat = 0, b2: CGFloat = 0, a2: CGFloat = 0
#if !canImport(UIKit)
let `self` = usingColorSpace(.sRGB)!
let target = target.usingColorSpace(.sRGB)!
#endif
self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
target.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
return Self(
red: r1 * (1.0 - amount) + r2 * amount,
green: g1 * (1.0 - amount) + g2 * amount,
blue: b1 * (1.0 - amount) + b2 * amount,
alpha: a1
)
}
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) }
}
You can mix any color with black/white to get a darker/lighter version:
Color.red.mix(with: .dark, by: 0.5) // 👈 Darker Red
Color.red.mix(with: .white, by: 0.5) // 👈 Ligher Red
⚠️ Requires previous extension
extension Color {
public func lighter(by amount: CGFloat = 0.2) -> Self { Self(NativeColor(self).lighter(by: amount)) }
public func darker(by amount: CGFloat = 0.2) -> Self { Self(NativeColor(self).darker(by: amount)) }
}
Upvotes: 24
Reputation: 12621
extension UIColor {
///Take a color and return a "somewhat darker" color
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("** OS problem")
return self
}
let nudged = b * 0.5
return UIColor(hue: h, saturation: s, brightness: nudged, alpha: a)
}
}
usage something.color = .yellow.darker
It's that easy.
As explained by @MarkA.Donohoe, if you prefer a function the correct naming in UIKit would be:
.adjustingBrightness(byMultiplier f: CGFloat)
ie, let nudged = b * f
Save you typing:
extension UIColor {
///Take a color and return a "somewhat darker" color
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("** OS problem")
return self
}
let nudged = b * 0.5
return UIColor(hue: h, saturation: s, brightness: nudged, alpha: a)
}
///Adjust color with UIKit style function naming :)
func adjustingBrightness(byMultiplier: CGFloat) -> 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("** OS problem")
return self
}
print(">>", h, s, b, a)
let nudged = b + ( (1.0 - b) * byMultiplier )
print(">> NEW B", nudged)
return UIColor(hue: h, saturation: s, brightness: nudged, alpha: a)
}
}
Note that when dealing with modern UIKit colors you almost always want something like
c = UIColor.label.resolvedColor(with: .current).adjusting ...etc
rather than
c = UIColor.label.adjusting ... etc
Google as needed.
Talk to your color designers, adjusting colors is not so easy conceptually. (How do you make black "brighter"? Who knows.)
Here's what you typically want in many or most cases when you want to "lighten up / brighten up" a color:
///Example. Original b is 0.2. Hence 0.8 "remaining". Pass in a fraction of 0.5. Half of 0.8 is 0.4, so result is 0.6
func adjustingBrightness(byFractionallyIncreasingTowardsOne fito: CGFloat) -> 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("** OS problem")
return self
}
print(">>", h, s, b, a)
let nudged = b + ( (1.0 - b) * fito )
print(">> NEW B", nudged)
return UIColor(hue: h, saturation: s, brightness: nudged, alpha: a)
}
Upvotes: 15
Reputation: 1021
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
Upvotes: 1
Reputation: 1158
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)
Upvotes: 1
Reputation: 1086
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
}
}
Upvotes: 61
Reputation: 2095
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
}
}
Upvotes: 3
Reputation: 416
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))
}
}
Upvotes: 15
Reputation: 13274
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;
}
Upvotes: 0
Reputation: 1810
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
}
}
Upvotes: 4
Reputation: 1929
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
}
}
Upvotes: 0
Reputation: 3465
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:
Upvotes: 182
Reputation: 1422
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:
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
}
}
}
Upvotes: 3
Reputation: 39
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
}
}
Upvotes: 3