Get lighter and darker color variations for a given UIColor
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:
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
}
}
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) }
}
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)
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))
}
}