3

How do I convert a UIColor to a hexadecimal string of 3/4/6/8 digits in Swift?

How do I get a spectific one? for example, get "#0000FFFF" by calling UIColor.blue.eightDigitsString

Please See this:

5.2. The RGB hexadecimal notations: #RRGGBB

The CSS hex color notation allows a color to be specified by giving the channels as hexadecimal numbers, which is similar to how colors are often written directly in computer code. It’s also shorter than writing the same color out in rgb() notation.

The syntax of a is a <hash-token> token whose value consists of 3, 4, 6, or 8 hexadecimal digits. In other words, a hex color is written as a hash character, "#", followed by some number of digits 0-9 or letters a-f (the case of the letters doesn’t matter - #00ff00 is identical to #00FF00).

The number of hex digits given determines how to decode the hex notation into an RGB color:

6 digits
The first pair of digits, interpreted as a hexadecimal number, specifies the red channel of the color, where 00 represents the minimum value and ff (255 in decimal) represents the maximum. The next pair of digits, interpreted in the same way, specifies the green channel, and the last pair specifies the blue. The alpha channel of the color is fully opaque. In other words, #00ff00 represents the same color as rgb(0 255 0) (a lime green).

8 digits
The first 6 digits are interpreted identically to the 6-digit notation. The last pair of digits, interpreted as a hexadecimal number, specifies the alpha channel of the color, where 00 represents a fully transparent color and ff represent a fully opaque color. In other words, #0000ffcc represents the same color as rgb(0 0 100% / 80%) (a slightly-transparent blue).

3 digits
This is a shorter variant of the 6-digit notation. The first digit, interpreted as a hexadecimal number, specifies the red channel of the color, where 0 represents the minimum value and f represents the maximum. The next two digits represent the green and blue channels, respectively, in the same way. The alpha channel of the color is fully opaque. This syntax is often explained by saying that it’s identical to a 6-digit notation obtained by "duplicating" all of the digits. For example, the notation #123 specifies the same color as the notation #112233. This method of specifying a color has lower "resolution" than the 6-digit notation; there are only 4096 possible colors expressible in the 3-digit hex syntax, as opposed to approximately 17 million in 6-digit hex syntax.

4 digits
This is a shorter variant of the 8-digit notation, "expanded" in the same way as the 3-digit notation is. The first digit, interpreted as a hexadecimal number, specifies the red channel of the color, where 0 represents the minimum value and f represents the maximum. The next three digits represent the green, blue, and alpha channels, respectively.

Now I already know how to convert a UIColor object to a 6-digits hex string. But I'm not sure how to convert it to a 3-digits/4-digits/8-digits hex string and what should be noticed.

guard let components = cgColor.components, components.count >= 3 else {
    return nil
}
let r = Float(components[0])
let g = Float(components[1])
let b = Float(components[2])
var a = Float(1.0)
if components.count >= 4 {
    a = Float(components[3])
}
if alpha {
    // rrggbbaa mode
    // is there any difference between rrggbbaa and aarrggbb?
    return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
} else {
    // rrggbb mode
    return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
}

NOTE: it's UIColor to string, not string to UIColor

Meniny
  • 660
  • 8
  • 22
  • 2
    Possible duplicate of [How to convert UIColor to HEX and display in NSLog](https://stackoverflow.com/questions/26341008/how-to-convert-uicolor-to-hex-and-display-in-nslog) –  May 10 '18 at 02:31
  • @user770 I mean, how do I get a specific mode of hex string? (one of "#000"/"#0000"/"#0F0F0F"/"#0000FFFF") See https://drafts.csswg.org/css-color/#hex-notation – Meniny May 10 '18 at 02:34
  • Instead of linking to documentation, add relevant pieces of it to the post. –  May 10 '18 at 02:36
  • 1
    *"How do I get a spectific one?"* - by writing code to give you what you want. There is no standard API to get a `UIColor` as a hex code. – rmaddy May 10 '18 at 02:38
  • @rmaddy yes, you are right. thank you. But not helping. – Meniny May 10 '18 at 02:39
  • Given your current question, it is helping. Your question is a bit vague in regards to what you actually need help with. There are many existing questions including code to convert a `UIColor` to a hex string of one form or another. So please edit your question to clarify what exactly you need help with. Narrow your question down to one specific issue. At the moment it is overly broad and shows no effort. Do you need to know how to add methods to `UIColor`? Do you need to know how to calculate a hex code from a `UIColor`? Anything else? – rmaddy May 10 '18 at 02:46
  • @rmaddy well, It's not like that. "by writing code to give you what you want", this can solve all the questions in stackoverflow. Don't be mean. – Meniny May 10 '18 at 02:51
  • Please read my last comment again. I'm trying to help you help us by pointing out what is missing from your question. Writing a clear, concise question about a clear specific issue will help you get a good answer. Your end goal is clear. But what you specifically need help with is unclear. It's assumed you already know how to convert a `UIColor` to at least one kind of hex string because such code can be found very easily with minimal searching. Given this, it's not obvious what you need help with. – rmaddy May 10 '18 at 02:58
  • @rmaddy thanks again. I already know how to convert a `UIColor` object to a 6-digits hex string. But I'm not sure how to convert to a 3-digits/4-digits and 8-digits hex string and what should be cautioned. – Meniny May 10 '18 at 03:02
  • 1
    @EliasAbel the opposite way https://stackoverflow.com/a/31782490/2303865 – Leo Dabus May 10 '18 at 03:03
  • @EliasAbel Excellent info. Please put those details in your question and include the code you've already written for the 6-digits. But what do you mean by "cautioned"? – rmaddy May 10 '18 at 03:04
  • @rmaddy sorry about I'm not really good at English. By that I mean something I should "noticed". – Meniny May 10 '18 at 03:07

2 Answers2

8

Here's an extension for UIColor that can provide hexStrings in many formats including 3, 4, 6, and 8 digit forms:

extension UIColor {
    enum HexFormat {
        case RGB
        case ARGB
        case RGBA
        case RRGGBB
        case AARRGGBB
        case RRGGBBAA
    }

    enum HexDigits {
        case d3, d4, d6, d8
    }

    func hexString(_ format: HexFormat = .RRGGBBAA) -> String {
        let maxi = [.RGB, .ARGB, .RGBA].contains(format) ? 16 : 256

        func toI(_ f: CGFloat) -> Int {
            return min(maxi - 1, Int(CGFloat(maxi) * f))
        }

        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var a: CGFloat = 0

        self.getRed(&r, green: &g, blue: &b, alpha: &a)

        let ri = toI(r)
        let gi = toI(g)
        let bi = toI(b)
        let ai = toI(a)

        switch format {
        case .RGB:       return String(format: "#%X%X%X", ri, gi, bi)
        case .ARGB:      return String(format: "#%X%X%X%X", ai, ri, gi, bi)
        case .RGBA:      return String(format: "#%X%X%X%X", ri, gi, bi, ai)
        case .RRGGBB:    return String(format: "#%02X%02X%02X", ri, gi, bi)
        case .AARRGGBB:  return String(format: "#%02X%02X%02X%02X", ai, ri, gi, bi)
        case .RRGGBBAA:  return String(format: "#%02X%02X%02X%02X", ri, gi, bi, ai)
        }
    }

    func hexString(_ digits: HexDigits) -> String {
        switch digits {
        case .d3: return hexString(.RGB)
        case .d4: return hexString(.RGBA)
        case .d6: return hexString(.RRGGBB)
        case .d8: return hexString(.RRGGBBAA)
        }
    }
}

Examples

print(UIColor.red.hexString(.d3))  // #F00
print(UIColor.red.hexString(.d4))  // #F00F
print(UIColor.red.hexString(.d6))  // #FF0000
print(UIColor.red.hexString(.d8))  // #FF0000FF

print(UIColor.green.hexString(.RGB))  // #0F0
print(UIColor.green.hexString(.ARGB))  // #F0F0
print(UIColor.green.hexString(.RGBA))  // #0F0F
print(UIColor.green.hexString(.RRGGBB))  // #00FF00
print(UIColor.green.hexString(.AARRGGBB))  // #FF00FF00
print(UIColor.green.hexString(.RRGGBBAA))  // #00FF00FF

print(UIColor(red: 0.25, green: 0.5, blue: 0.75, alpha: 0.3333).hexString()) // #4080c055
vacawama
  • 150,663
  • 30
  • 266
  • 294
0

Any UIColor instance can be represented by 8 hexadecimal digits: for example #336699CC. For some colours, a shorter representation can be used:

  • for opaque colours (alpha unspecified or 1.0), the alpha component can be left off: #336699FF becomes #336699
  • if all colour components consist of pairs of the same digit, the digit only needs to be specified once: #336699CC becomes #369C, but #335799CC cannot be shortened
  • the two rules above can be combined: #336699FF becomes #369

The following function will return the shortest valid representation allowed for a given UIColor.

struct HexRepresentationOptions: OptionSet {
    let rawValue: UInt

    static let allowImplicitAlpha = HexRepresentationOptions(rawValue: 1 << 0)
    static let allowShortForm = HexRepresentationOptions(rawValue: 1 << 1)

    static let allowAll: HexRepresentationOptions = [
        .allowImplicitAlpha, 
        .allowShortForm
    ]
}

func hexRepresentation(forColor color: UIColor, 
                       options: HexRepresentationOptions = .allowAll) -> String? {
    var red: CGFloat = 0.0
    var green: CGFloat = 0.0
    var blue: CGFloat = 0.0
    var alpha: CGFloat = 0.0

    guard color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) else {
        return nil
    }

    let colorComponents: [CGFloat]
    if options.contains(.allowImplicitAlpha) && alpha == 1.0 {
        colorComponents = [red, green, blue]
    } else {
        colorComponents = [red, green, blue, alpha]
    }

    let hexComponents = colorComponents.map { component -> (UInt8, UInt8, UInt8) in
        let hex = UInt8(component * 0xFF)
        return (hex, hex & 0x0F, hex >> 4)
    }

    let hasAlpha = colorComponents.count == 4
    let useShortForm = options.contains(.allowShortForm) && 
        !hexComponents.contains(where: { c in c.1 != c.2 })

    let hexColor: UInt64 = hexComponents.reduce(UInt64(0)) { result, component in
        if useShortForm {
            return (result << 4) | UInt64(component.1)
        } else {
            return (result << 8) | UInt64(component.0)
        }
    }

    switch (useShortForm, hasAlpha) {
    case (true, false):
        return String(format: "#%03X", hexColor)
    case (true, true):
        return String(format: "#%04X", hexColor)
    case (false, false):
        return String(format: "#%06X", hexColor)
    case (false, true):
        return String(format: "#%08X", hexColor)
    }
}
Rein Spijkerman
  • 604
  • 5
  • 15