简体   繁体   English

如何从 SwiftUI 中的 Color 获取 RGB 组件

[英]How to get RGB components from Color in SwiftUI

If I have a SwiftUI Color :如果我有 SwiftUI Color

let col: Color = Color(red: 0.5, green: 0.5, blue: 0.5)

How do I get the RGB components from col ?如何从col获取 RGB 分量?
Like this maybe:像这样也许:

print(col.components.red)

In UIKit, I could use UIColor.getRed but there doesn't seem to be an equivalent in SwiftUI.在 UIKit 中,我可以使用UIColor.getRed但在 SwiftUI 中似乎没有等价物。

iOS 14 / macOS 10.16 iOS 14 / macOS 10.16

There is a new initializer that takes a Color and returns a UIColor for iOS or NSColor for macOS now.现在有一个新的初始化器,它接受一个Color并返回一个UIColor for iOSNSColor for macOS With the help of those you can implement the following extensions:在这些人的帮助下,您可以实现以下扩展:

iOS / 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)
    }
}

Usage用法

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等待 API 我滥用CustomStringConvertible协议用于简单的 rgba 案例,其中颜色描述格式为 #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如您所见,Color.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
    }
}

You can use UIColor and transform the UIColor to Color after.您可以使用 UIColor 并将 UIColor 转换为 Color 之后。 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)
    }
}

The answer is no - there's no API do so (yet), but...答案是否定的- 没有 API 这样做(还),但是......

Most of SwiftUI structs have fields that are private , like in Color .大多数 SwiftUI 结构都具有private字段,例如Color

You can use Mirror to extract such informations - but keep in mind it is not efficient.您可以使用Mirror来提取此类信息 - 但请记住它效率不高。

Here's how to extract the hexadecimal representation of a SwiftUI Color - for educational purpose.以下是如何提取 SwiftUI Color的十六进制表示 - 用于教育目的。

Copy and paste this into a Xcode 11 playground.将其复制并粘贴到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 .喜欢的颜色.red.white等,似乎没有对他们有许多信息,当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.red / blue / green组件实例化的Color代替。

▿ #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:在 Playground 中,您将看到:

  • systemColor.hexRepresentation returning nil systemColor.hexRepresentation返回nil
  • color.hexRepresentation returning "#4C80FFFF" color.hexRepresentation返回"#4C80FFFF"

Based on @Mojtaba's answer, I came up with a shorter, more flexible version:根据@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
    }
}

Doing asNative.redComponent etc. might also work, FYI.做 asNative.redComponent 等也可能有效,仅供参考。

Simple one-liner:简单的单线:

print(UIColor(Color.blue).cgColor.components)

You get an [CGFloat]?你得到一个[CGFloat]? of [red, green, blue, alpha]. [红色,绿色,蓝色,alpha]。

I have found that @Mojtaba Hosseinis answer is working fine, except when you have your colors declared inside assets with light and dark appearances.我发现@Mojtaba Hosseinis 的答案工作正常,除非您在资产中声明了颜色的明暗外观。

Then I found that the dark appearance somehow gets lost when using UIColor(self) .然后我发现使用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.请注意,这只是针对iOS ,因为我的应用程序是iOS唯一,你当然可以做同样的@Mojtaba侯赛尼和它适应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))
    }
}

The idea is to use the UIColor(named:) initializer instead, where all appearances are correct.这个想法是使用UIColor(named:)初始值设定项,所有外观都是正确的。 Fortunately, the name we set in assets is saved in the description of the Color .幸运的是,我们在 assets 中设置的名称保存在Color的描述中。 We only have to abstract it since there is also other information, namely bundle, etc.我们只需要抽象它,因为还有其他信息,即 bundle 等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM