Skip to content

Commit ba9a047

Browse files
add method for blending colors
1 parent 4bf94fd commit ba9a047

File tree

1 file changed

+84
-23
lines changed

1 file changed

+84
-23
lines changed

Sources/ComponentsKit/Shared/Colors/UniversalColor.swift

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,47 @@ public struct UniversalColor: Hashable {
8686
return UIColor(color)
8787
}
8888
}
89+
90+
/// Returns a tuple containing the red, green, blue, and alpha components of the color.
91+
private var rgba: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
92+
switch self {
93+
case let .rgba(r, g, b, a):
94+
return (r, g, b, a)
95+
case .uiColor, .color:
96+
var red: CGFloat = 0
97+
var green: CGFloat = 0
98+
var blue: CGFloat = 0
99+
var alpha: CGFloat = 0
100+
101+
self.uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
102+
103+
return (red, green, blue, alpha)
104+
}
105+
}
106+
107+
/// Returns a new `ColorRepresentable` by blending the current color with another color.
108+
///
109+
/// The blending is performed using the alpha value of the current color,
110+
/// where the second color is treated as fully opaque (alpha = `1.0 - self.alpha`).
111+
///
112+
/// The resulting color's RGBA components are calculated as:
113+
/// - `red = self.red * self.alpha + other.red * (1.0 - self.alpha)`
114+
/// - `green = self.green * self.alpha + other.green * (1.0 - self.alpha)`
115+
/// - `blue = self.blue * self.alpha + other.blue * (1.0 - self.alpha)`
116+
/// - The resulting color's alpha will always be `1.0`.
117+
///
118+
/// - Parameter other: The `ColorRepresentable` to blend with the current color.
119+
/// - Returns: A new `ColorRepresentable` instance representing the blended color.
120+
fileprivate func blended(with other: Self) -> Self {
121+
let rgba = self.rgba
122+
let otherRgba = other.rgba
123+
124+
let red = rgba.r * rgba.a + otherRgba.r * (1.0 - rgba.a)
125+
let green = rgba.g * rgba.a + otherRgba.g * (1.0 - rgba.a)
126+
let blue = rgba.b * rgba.a + otherRgba.b * (1.0 - rgba.a)
127+
128+
return .rgba(r: red, g: green, b: blue, a: 1.0)
129+
}
89130
}
90131

91132
// MARK: - Properties
@@ -119,29 +160,6 @@ public struct UniversalColor: Hashable {
119160
return Self(light: universal, dark: universal)
120161
}
121162

122-
// MARK: - Methods
123-
124-
/// Returns a new `UniversalColor` with the specified opacity.
125-
///
126-
/// - Parameter alpha: The desired opacity (0.0–1.0).
127-
/// - Returns: A new `UniversalColor` instance with the adjusted opacity.
128-
public func withOpacity(_ alpha: CGFloat) -> Self {
129-
return .init(
130-
light: self.light.withOpacity(alpha),
131-
dark: self.dark.withOpacity(alpha)
132-
)
133-
}
134-
135-
/// Returns a disabled version of the color based on a global opacity configuration.
136-
///
137-
/// - Parameter isEnabled: A Boolean value indicating whether the color should be enabled.
138-
/// - Returns: A new `UniversalColor` instance with reduced opacity if `isEnabled` is `false`.
139-
public func enabled(_ isEnabled: Bool) -> Self {
140-
return isEnabled
141-
? self
142-
: self.withOpacity(ComponentsKitConfig.shared.layout.disabledOpacity)
143-
}
144-
145163
// MARK: - Colors
146164

147165
/// Returns the `UIColor` representation of the color.
@@ -167,4 +185,47 @@ public struct UniversalColor: Hashable {
167185
public var cgColor: CGColor {
168186
return self.uiColor.cgColor
169187
}
188+
189+
// MARK: - Methods
190+
191+
/// Returns a new `UniversalColor` with the specified opacity.
192+
///
193+
/// - Parameter alpha: The desired opacity (0.0–1.0).
194+
/// - Returns: A new `UniversalColor` instance with the adjusted opacity.
195+
public func withOpacity(_ alpha: CGFloat) -> Self {
196+
return .init(
197+
light: self.light.withOpacity(alpha),
198+
dark: self.dark.withOpacity(alpha)
199+
)
200+
}
201+
202+
/// Returns a disabled version of the color based on a global opacity configuration.
203+
///
204+
/// - Parameter isEnabled: A Boolean value indicating whether the color should be enabled.
205+
/// - Returns: A new `UniversalColor` instance with reduced opacity if `isEnabled` is `false`.
206+
public func enabled(_ isEnabled: Bool) -> Self {
207+
return isEnabled
208+
? self
209+
: self.withOpacity(ComponentsKitConfig.shared.layout.disabledOpacity)
210+
}
211+
212+
/// Returns a new `UniversalColor` by blending the current color with another color.
213+
///
214+
/// The blending is performed using the alpha value of the current color,
215+
/// where the second color is treated as fully opaque (alpha = `1.0 - self.alpha`).
216+
///
217+
/// The resulting color's RGBA components are calculated as:
218+
/// - `red = self.red * self.alpha + other.red * (1.0 - self.alpha)`
219+
/// - `green = self.green * self.alpha + other.green * (1.0 - self.alpha)`
220+
/// - `blue = self.blue * self.alpha + other.blue * (1.0 - self.alpha)`
221+
/// - The resulting color's alpha will always be `1.0`.
222+
///
223+
/// - Parameter other: The `UniversalColor` to blend with the current color.
224+
/// - Returns: A new `UniversalColor` instance representing the blended color.
225+
public func blended(with other: Self) -> Self {
226+
return .init(
227+
light: self.light.blended(with: other.light),
228+
dark: self.dark.blended(with: other.dark)
229+
)
230+
}
170231
}

0 commit comments

Comments
 (0)