如何在 SwiftUI 中从 Color 获取 RGB 分量
How to get RGB components from Color in SwiftUI
如果我有 SwiftUI Color
:
let col: Color = Color(red: 0.5, green: 0.5, blue: 0.5)
如何从 col
获取 RGB 分量?
可能像这样:
print(col.components.red)
在 UIKit 中,我可以使用 UIColor.getRed
,但在 SwiftUI 中似乎没有等效项。
答案是否 - 还没有API这样做,但是...
大多数 SwiftUI 结构都有 private
的字段,例如 Color
.
您可以使用 Mirror
来提取此类信息 - 但请记住它效率不高。
以下是提取 SwiftUI 的十六进制表示的方法 Color
- 用于教育目的。
将其复制并粘贴到 Xcode 11 游乐场。
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 { [=10=].label == "provider" }.first
guard let provider = _provider?.value else {
return nil
}
let providerChildren = Mirror(reflecting: provider).children
let _base = providerChildren.filter { [=10=].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
像.red
、.white
等颜色,好像没有多少信息,当dumped
.
只是他们的 "system" 名字。
▿ red
▿ provider: SwiftUI.(unknown context at 97483bc).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.(unknown context at 9748300).AnyColorBox
- base: SwiftUI.SystemColorType.red
用 red
/blue
/green
组件实例化的 Color
代替。
▿ #4C80FFFF
▿ provider: SwiftUI.(unknown context at cd2e3bc).ColorBox<SwiftUI.Color._Resolved> #0
- super: SwiftUI.(unknown context at cd2e300).AnyColorBox
▿ base: #4C80FFFF
- linearRed: 0.073238954
- linearGreen: 0.21404114
- linearBlue: 1.0
- opacity: 1.0
在 Playground 中,您将看到:
systemColor.hexRepresentation
返回 nil
color.hexRepresentation
返回 "#4C80FFFF"
等待 API 我滥用了 CustomStringConvertible
颜色描述格式为 #rrggbbaa
的简单 rgba 协议
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)"
如您所见,Color.red 之类的东西只是转储 "red" 但如果您正在使用
由代码生成的简单 RGB 颜色(即来自颜色选择器)那么这还不错
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
}
}
您可以使用 UIColor 并将 UIColor 转换为 Color。
代码:
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)
}
}
iOS 14 / macOS 10.16
有一个新的初始化器,它接受一个 Color
和 returns 一个 UIColor
for iOS or NSColor
现在 macOS。在这些人的帮助下,您可以实现以下扩展:
iOS / macOS
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!
我发现@Mojtaba Hosseinis 的回答工作正常,除非您在具有明暗外观的资产中声明了颜色。
然后我发现使用 UIColor(self)
时,黑色外观不知何故消失了。这是我想出的解决方法:
请注意,这仅适用于 iOS
,因为我的应用仅适用于 iOS
,您当然可以像@Mojtaba Hosseini 那样做,并将其调整为 macOS
。
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))
}
}
想法是改用 UIColor(named:)
初始值设定项,其中所有外观都是正确的。
幸运的是,我们在assets中设置的名称保存在Color
的描述中。我们只需要抽象它,因为还有其他信息,即bundle等
根据@Mojtaba 的回答,我想出了一个更短、更灵活的版本:
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
extension Color {
#if canImport(UIKit)
var asNative: UIColor { UIColor(self) }
#elseif canImport(AppKit)
var asNative: NSColor { NSColor(self) }
#endif
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
let color = asNative.usingColorSpace(.deviceRGB)!
var t = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
color.getRed(&t.0, green: &t.1, blue: &t.2, alpha: &t.3)
return t
}
var hsva: (hue: CGFloat, saturation: CGFloat, value: CGFloat, alpha: CGFloat) {
let color = asNative.usingColorSpace(.deviceRGB)!
var t = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
color.getHue(&t.0, saturation: &t.1, brightness: &t.2, alpha: &t.3)
return t
}
}
做 asNative.redComponent 等也可能有效,仅供参考。
简单的一行:
print(UIColor(Color.blue).cgColor.components)
你得到 [CGFloat]?
的 [red, green, blue, alpha]。
如果我有 SwiftUI Color
:
let col: Color = Color(red: 0.5, green: 0.5, blue: 0.5)
如何从 col
获取 RGB 分量?
可能像这样:
print(col.components.red)
在 UIKit 中,我可以使用 UIColor.getRed
,但在 SwiftUI 中似乎没有等效项。
答案是否 - 还没有API这样做,但是...
大多数 SwiftUI 结构都有 private
的字段,例如 Color
.
您可以使用 Mirror
来提取此类信息 - 但请记住它效率不高。
以下是提取 SwiftUI 的十六进制表示的方法 Color
- 用于教育目的。
将其复制并粘贴到 Xcode 11 游乐场。
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 { [=10=].label == "provider" }.first
guard let provider = _provider?.value else {
return nil
}
let providerChildren = Mirror(reflecting: provider).children
let _base = providerChildren.filter { [=10=].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
像.red
、.white
等颜色,好像没有多少信息,当dumped
.
只是他们的 "system" 名字。
▿ red
▿ provider: SwiftUI.(unknown context at 97483bc).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.(unknown context at 9748300).AnyColorBox
- base: SwiftUI.SystemColorType.red
用 red
/blue
/green
组件实例化的 Color
代替。
▿ #4C80FFFF
▿ provider: SwiftUI.(unknown context at cd2e3bc).ColorBox<SwiftUI.Color._Resolved> #0
- super: SwiftUI.(unknown context at cd2e300).AnyColorBox
▿ base: #4C80FFFF
- linearRed: 0.073238954
- linearGreen: 0.21404114
- linearBlue: 1.0
- opacity: 1.0
在 Playground 中,您将看到:
systemColor.hexRepresentation
返回nil
color.hexRepresentation
返回"#4C80FFFF"
等待 API 我滥用了 CustomStringConvertible
颜色描述格式为 #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)"
如您所见,Color.red 之类的东西只是转储 "red" 但如果您正在使用 由代码生成的简单 RGB 颜色(即来自颜色选择器)那么这还不错
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
}
}
您可以使用 UIColor 并将 UIColor 转换为 Color。 代码:
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)
}
}
iOS 14 / macOS 10.16
有一个新的初始化器,它接受一个 Color
和 returns 一个 UIColor
for iOS or NSColor
现在 macOS。在这些人的帮助下,您可以实现以下扩展:
iOS / macOS
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!
我发现@Mojtaba Hosseinis 的回答工作正常,除非您在具有明暗外观的资产中声明了颜色。
然后我发现使用 UIColor(self)
时,黑色外观不知何故消失了。这是我想出的解决方法:
请注意,这仅适用于 iOS
,因为我的应用仅适用于 iOS
,您当然可以像@Mojtaba Hosseini 那样做,并将其调整为 macOS
。
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))
}
}
想法是改用 UIColor(named:)
初始值设定项,其中所有外观都是正确的。
幸运的是,我们在assets中设置的名称保存在Color
的描述中。我们只需要抽象它,因为还有其他信息,即bundle等
根据@Mojtaba 的回答,我想出了一个更短、更灵活的版本:
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
extension Color {
#if canImport(UIKit)
var asNative: UIColor { UIColor(self) }
#elseif canImport(AppKit)
var asNative: NSColor { NSColor(self) }
#endif
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
let color = asNative.usingColorSpace(.deviceRGB)!
var t = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
color.getRed(&t.0, green: &t.1, blue: &t.2, alpha: &t.3)
return t
}
var hsva: (hue: CGFloat, saturation: CGFloat, value: CGFloat, alpha: CGFloat) {
let color = asNative.usingColorSpace(.deviceRGB)!
var t = (CGFloat(), CGFloat(), CGFloat(), CGFloat())
color.getHue(&t.0, saturation: &t.1, brightness: &t.2, alpha: &t.3)
return t
}
}
做 asNative.redComponent 等也可能有效,仅供参考。
简单的一行:
print(UIColor(Color.blue).cgColor.components)
你得到 [CGFloat]?
的 [red, green, blue, alpha]。