【问题标题】:How to get RGB components from Color in SwiftUI如何在 SwiftUI 中从颜色中获取 RGB 分量
【发布时间】:2019-06-13 17:51:04
【问题描述】:

如果我有 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 中似乎没有等效项。

【问题讨论】:

    标签: swift colors rgb swiftui


    【解决方案1】:

    iOS 14 / macOS 10.16

    现在有一个新的初始化程序接受 Color 并返回 UIColor 用于 iOSNSColor 用于 ma​​cOS。在这些人的帮助下,您可以实现以下扩展:

    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!
    

    【讨论】:

    • 我发现这不知何故失去了资产中设置的黑暗外观?
    • 我已经设法解决了这个问题,请参阅下面的答案。编辑太多了。
    【解决方案2】:

    等待 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 之类的内容只是转储“红色”,但如果您正在使用 由代码(即来自颜色选择器)生成的简单 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
        }
    }
    

    【讨论】:

      【解决方案3】:

      简单的单行:

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

      你会得到一个 [red, green, blue, alpha] 的 [CGFloat]?

      【讨论】:

        【解决方案4】:

        答案是 - (目前)还没有 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 { $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
        

        .red.white 等颜色似乎没有太多信息,当dumped 时。

        只是他们的“系统”名称。

        ▿ red
          ▿ provider: SwiftUI.(unknown context at $1297483bc).ColorBox<SwiftUI.SystemColorType> #0
            - super: SwiftUI.(unknown context at $129748300).AnyColorBox
            - base: SwiftUI.SystemColorType.red
        

        使用 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
        

        在 Playground 中,您会看到:

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

        【讨论】:

        • 我觉得这个问题的答案应该是“你不知道”。 SwiftUI 中的数据流从数据进入视图。您不会从视图中获取数据。您将数据放入视图中。如果你有一种颜色,那么它只存在,因为你给了它 rgb 值。所以你已经有了数据。
        • @Fogmeister 好点 - 但深入研究私有实现总是很有趣:)
        • 呵呵很好找:)
        • @Fogmeister 我同意一般观点,但你建议我如何在代码中存储颜色?假设我有一个具有背景颜色的“页面”模型。我应该创建自己的 Color 结构,然后在视图中转换为 SwiftUI 颜色吗?这似乎是一个框架应该提供的东西......
        【解决方案5】:

        您可以使用 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)
            }
        }
        

        【讨论】:

          【解决方案6】:

          我发现@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 的描述中。我们只需要抽象它,因为还有其他信息,即捆绑等。

          【讨论】:

            【解决方案7】:

            根据@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 等也可能有效,仅供参考。

            【讨论】:

              猜你喜欢
              • 2014-06-21
              • 2011-04-01
              • 2012-06-13
              • 2012-02-07
              • 1970-01-01
              • 2013-07-03
              • 1970-01-01
              • 2016-08-10
              • 1970-01-01
              相关资源
              最近更新 更多