If I have a SwiftUI Color:
let col: Color = Color(red: 0.5, green: 0.5, blue: 0.5)
How do I get the RGB components from
You can use UIColor and transform the UIColor to Color after. Code:
extension UIColor {
func hexValue() -> String {
let values = self.cgColor.components
var outputR: Int = 0
var outputG: Int = 0
var outputB: Int = 0
var outputA: Int = 1
switch values!.count {
case 1:
outputR = Int(values![0] * 255)
outputG = Int(values![0] * 255)
outputB = Int(values![0] * 255)
outputA = 1
case 2:
outputR = Int(values![0] * 255)
outputG = Int(values![0] * 255)
outputB = Int(values![0] * 255)
outputA = Int(values![1] * 255)
case 3:
outputR = Int(values![0] * 255)
outputG = Int(values![1] * 255)
outputB = Int(values![2] * 255)
outputA = 1
case 4:
outputR = Int(values![0] * 255)
outputG = Int(values![1] * 255)
outputB = Int(values![2] * 255)
outputA = Int(values![3] * 255)
default:
break
}
return "#" + String(format:"%02X", outputR) + String(format:"%02X", outputG) + String(format:"%02X", outputB) + String(format:"%02X", outputA)
}
}
I have found that @Mojtaba Hosseinis answer is working fine, except when you have your colors declared inside assets with light and dark appearances.
Then I found that the dark appearance somehow gets lost when using UIColor(self). Here is a workaround I came up with:
Note, this is only for iOS since my app is iOS only, you could of course do the same as @Mojtaba Hosseini and adapt it to macOS as well.
extension Color {
var components: (r: Double, g: Double, b: Double, o: Double)? {
let uiColor: UIColor
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var o: CGFloat = 0
if self.description.contains("NamedColor") {
let lowerBound = self.description.range(of: "name: \"")!.upperBound
let upperBound = self.description.range(of: "\", bundle")!.lowerBound
let assetsName = String(self.description[lowerBound..<upperBound])
uiColor = UIColor(named: assetsName)!
} else {
uiColor = UIColor(self)
}
guard uiColor.getRed(&r, green: &g, blue: &b, alpha: &o) else { return nil }
return (Double(r), Double(g), Double(b), Double(o))
}
}
The idea is to use the UIColor(named:) initializer instead, where all appearances are correct.
Fortunately, the name we set in assets is saved in the description of the Color. We only have to abstract it since there is also other information, namely bundle, etc.
There is a new initializer that takes a Color and returns a UIColor for iOS or NSColor for macOS now. With the help of those you can implement the following extensions:
import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
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 {
// You can handle the failure here as you want
return (0, 0, 0, 0)
}
return (r, g, b, o)
}
}
Color.red.components.red // 0.9999999403953552 // <- SwiftUI Colors are not pure!
Waiting for an API I've abused CustomStringConvertible protocol for the simple rgba case where the color description format is #rrggbbaa
debugPrint(Color.red)
debugPrint(Color(red: 1.0, green: 0.0, blue: 0.0))
debugPrint(Color(red: 1.0, green: 0.3, blue: 0.0))
debugPrint(Color(.sRGB, red: 1.0, green: 0.0, blue: 0.5, opacity: 0.3))
debugPrint(Color(hue: 1.0, saturation: 0.0, brightness: 1.0))
debugPrint(Color(.displayP3, red: 1.0, green: 0.0, blue: 0.0, opacity: 1.0).description)
red
#FF0000FF
#FF4C00FF
#FF00804D
#FFFFFFFF
"DisplayP3(red: 1.0, green: 0.0, blue: 0.0, opacity: 1.0)"
as you can see, things like Color.red just dump "red" but if you are working with simple RGB colors generated by code (ie from a color picker) then this is not too bad
extension SwiftUI.Color {
var redComponent: Double? {
let val = description
guard val.hasPrefix("#") else { return nil }
let r1 = val.index(val.startIndex, offsetBy: 1)
let r2 = val.index(val.startIndex, offsetBy: 2)
return Double(Int(val[r1...r2], radix: 16)!) / 255.0
}
var greenComponent: Double? {
let val = description
guard val.hasPrefix("#") else { return nil }
let g1 = val.index(val.startIndex, offsetBy: 3)
let g2 = val.index(val.startIndex, offsetBy: 4)
return Double(Int(val[g1...g2], radix: 16)!) / 255.0
}
var blueComponent: Double? {
let val = description
guard val.hasPrefix("#") else { return nil }
let b1 = val.index(val.startIndex, offsetBy: 5)
let b2 = val.index(val.startIndex, offsetBy: 6)
return Double(Int(val[b1...b2], radix: 16)!) / 255.0
}
var opacityComponent: Double? {
let val = description
guard val.hasPrefix("#") else { return nil }
let b1 = val.index(val.startIndex, offsetBy: 7)
let b2 = val.index(val.startIndex, offsetBy: 8)
return Double(Int(val[b1...b2], radix: 16)!) / 255.0
}
}
The answer is no - there's no API do so (yet), but...
Most of SwiftUI structs have fields that are private, like in Color.
You can use Mirror to extract such informations - but keep in mind it is not efficient.
Here's how to extract the hexadecimal representation of a SwiftUI Color - for educational purpose.
Copy and paste this into a Xcode 11 playground.
import UIKit
import SwiftUI
let systemColor = Color.red
let color = Color(red: 0.3, green: 0.5, blue: 1)
extension Color {
var hexRepresentation: String? {
let children = Mirror(reflecting: color).children
let _provider = children.filter { $0.label == "provider" }.first
guard let provider = _provider?.value else {
return nil
}
let providerChildren = Mirror(reflecting: provider).children
let _base = providerChildren.filter { $0.label == "base" }.first
guard let base = _base?.value else {
return nil
}
var baseValue: String = ""
dump(base, to: &baseValue)
guard let firstLine = baseValue.split(separator: "\n").first,
let hexString = firstLine.split(separator: " ")[1] as Substring? else {
return nil
}
return hexString.trimmingCharacters(in: .newlines)
}
}
systemColor.hexRepresentation
color.hexRepresentation
Colors like .red, .white, etc., don't seem to have many information in them, when dumped.
Just their "system" name.
▿ red
▿ provider: SwiftUI.(unknown context at $1297483bc).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.(unknown context at $129748300).AnyColorBox
- base: SwiftUI.SystemColorType.red
A Color instantiated with red/blue/green components does instead.
▿ #4C80FFFF
▿ provider: SwiftUI.(unknown context at $11cd2e3bc).ColorBox<SwiftUI.Color._Resolved> #0
- super: SwiftUI.(unknown context at $11cd2e300).AnyColorBox
▿ base: #4C80FFFF
- linearRed: 0.073238954
- linearGreen: 0.21404114
- linearBlue: 1.0
- opacity: 1.0
In the Playground, you will see:
systemColor.hexRepresentation returning nilcolor.hexRepresentation returning "#4C80FFFF"