How to get RGB components from Color SwiftUI

后端 未结 5 931
旧时难觅i
旧时难觅i 2020-12-15 06:07

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

相关标签:
5条回答
  • 2020-12-15 06:23

    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)
        }
    }
    
    0 讨论(0)
  • 2020-12-15 06:27

    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.

    0 讨论(0)
  • 2020-12-15 06:29

    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. With the help of those you can implement the following extensions:

    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!
    
    0 讨论(0)
  • 2020-12-15 06:31

    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
        }
    }
    
    0 讨论(0)
  • 2020-12-15 06:31

    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 nil
    • color.hexRepresentation returning "#4C80FFFF"
    0 讨论(0)
提交回复
热议问题