UIColorと16進整数・カラーコード文字列との相互変換

#ごめんなさい

ここでRGBAの値をIntで扱っていますが、
RGBAの値はIntだとオーバーフローしてしまうことに気づいてしまいました

UInt32に置き換えれば動くのですが
キャスト処理などをこの記事のソースコード全体に行わなければなりません

またどこかで修正します・・・
(2016.07.1 加筆)

#まえがき
役に立つかどうかよくわからないTipsシリーズ
今回少し長めです

色を扱うクラスは `UIColor` です
これはこれで標準でも便利なクラスですが
もっとお手軽に使えるように色々と拡張してみます

##下ごしらえ
UIColor の便利な拡張のために
まずはそれ用のファイルを作ります
`UIColor+Utility.swift`とでも名づけます

そして、まずファイル内だけで使うプライベートな定義します

```UIColor+Utility.swift
extension UIColor {
    
    private static let maxHex = 0xFFFFFFFF
    private static let minHex = 0x0
    
    private static let prefix = "#"
    
    private class func substring(string: String, location: Int, length: Int? = nil) -> String {
        let strlen = string.characters.count
        var len = length ?? strlen
        if location + len > strlen {
            len = strlen - location
        }
        return (string as NSString).substringWithRange(NSMakeRange(location, len))
    }
    
    private class func intByCGFloat(v: CGFloat) -> Int {
        return Int(round(v * 255.0))
    }
    
    private class func hexStringByCGFloat(v: CGFloat) -> String {
        let n = self.intByCGFloat(v)
        return NSString(format: "%02X", n) as String
    }
}
```

`substring`メソッドは
文字列操作でよくある指定箇所の取得です
むしろ String の extension として別で定義してもいいかもしれませんが、
今回はこのファイルだけで完結するように UIColor のプライベートメソッドとして定義します


`intByCGFloat`メソッド、`hexStringByCGFloat `メソッドは
0.0〜1.0 の CGFloat の値を 0〜255 の値に変換するものと、
その値を 16進数 の文字列に変換するものです

あとは内部で使う定数になります

##RGBA構成を取得する

次に以下のようなメソッドを追加します

```UIColor+Utility.swift
extension UIColor {
    
    var floatValues: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        return (r, g, b, a)
    }
    
    var intValues: (red: Int, green: Int, blue: Int, alpha: Int) {
        let f = self.floatValues
        return (
            UIColor.intByCGFloat(f.red),
            UIColor.intByCGFloat(f.green),
            UIColor.intByCGFloat(f.blue),
            UIColor.intByCGFloat(f.alpha)
        )
    }
}
```

先ほど定義したのはプライベートなクラスメソッドですが、
これらはパブリックなインスタンスプロパティです

もともと UIColor には

```swift
public func getRed(
    red:   UnsafeMutablePointer<CGFloat>,
    green: UnsafeMutablePointer<CGFloat>,
    blue:  UnsafeMutablePointer<CGFloat>,
    alpha: UnsafeMutablePointer<CGFloat>
) -> Bool
```
というメソッドが用意されていますが
これをそのまま使うのは面倒くさいので
`floatValues` というプロパティで
赤・緑・青・アルファ値という構成のタプルで返してくれるようにします

```swift
let red = UIColor.brownColor().floatValues.red
```
このように書くと、プリセットされている茶色の赤(R)要素は、0.6であるという風に取得ができます
つまり`赤色 60%`ということですね

`intValues` というプロパティでは、255を最大値とした要素の値が取得できます

```swift
let red = UIColor.brownColor().intValues.red
```
上のように書くと 255 の 60% で 153 という数値が取得できます

これらは次以降の処理でも多用するのですが
このメソッド自体が便利なのでパブリックとして定義しておきます

##整数とUIColorの相互変換

このあたりから本題になります

UIColor自体は、赤と緑と青、いわゆるRGBの値をそれぞれ渡す形でインスタンスを作ることが多いです
(HSVやグレースケールもありますが)

しかし、デザイナさんとやりとりするためには
HTMLでよく使うカラーコードをもって色の定義をしたほうがメンテナンスがしやすいです

ここでは前項までに定義したメソッドやプロパティを使用して
16進数で定義された整数をもって UIColor を初期化する方法と
逆に UIColor から整数を返すメソッドを定義します

```UIColor+Utility.swift
extension UIColor {
    
    convenience init(rgb: Int) {
        let r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
        let g = CGFloat((rgb & 0x00FF00) >>  8) / 255.0
        let b = CGFloat( rgb & 0x0000FF       ) / 255.0
        self.init(red: r, green: g, blue: b, alpha: 1.0)
    }
    
    var rgb: Int {
        let i = self.intValues
        return (i.red * 0x010000) + (i.green * 0x000100) + i.blue
    }
    
    var rgbString: String {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        
        var ret = ""
        ret += UIColor.hexStringByCGFloat(r)
        ret += UIColor.hexStringByCGFloat(g)
        ret += UIColor.hexStringByCGFloat(b)
        return ret
    }
}
```

コンビニエンスコンストラクタである `init(rgb:)` ですが
ここでいう Int型の値は 1 とか 123 とかいう値ではなく
`0x6789AB` というような16進数ベースの値を渡すことを期待しています

例を書きます

```swift
let white = UIColor(rgb: 0xFFFFFF)
```

これで白色の UIColor が戻ります
人の目に優しくなりました

例えば色を定数で定義しておきたい時に

```swift
// before
let headerColor = UIColor(red: 0.274, green: 0.811, blue: 0.239, alpha: 1.0)

// after
let headerColor = UIColor(rgb: 0x46CF3D)
```

どちらのほうがデザイナさんに優しいかは一目瞭然です


`rgb`プロパティと、`rgbString`プロパティは
逆にUIColorを整数、またはカラーコード文字列で取得するためのものです

```swift
let headerColor = UIColor(rgb: 0x46CF3D)
print(headerColor.rgb) // 4640573を出力
print(headerColor.rgbString) // "46CF3D"を出力
```

このように取得できます

用途としては、DBなどに保存するのに使ったり、あるいは表示をしたり
といったところでしょうか

##アルファ値も含めたRGBA値で扱う

前項は RGB値の整数と UIColor の変換を行いましたが、
透過率であるアルファ値(A値)も UIColor には大事な要素ですね

RGB値だけではなく、RGBA値でも同じように使えるようにしてみます

```UIColor+Utility.swift
extension UIColor {
    
    convenience init(rgba: Int) {
        let r = CGFloat((rgba & 0xFF000000) >> 24) / 255.0
        let g = CGFloat((rgba & 0x00FF0000) >> 16) / 255.0
        let b = CGFloat((rgba & 0x0000FF00) >>  8) / 255.0
        let a = CGFloat( rgba & 0x000000FF       ) / 255.0
        self.init(red: r, green: g, blue: b, alpha: a)
    }
    
    var rgba: Int {
        let i = self.intValues
        return (i.red * 0x01000000) + (i.green * 0x00010000) + (i.blue *  0x00000100) + i.alpha
    }
    
    var rgbaString: String {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        
        var ret = ""
        ret += UIColor.hexStringByCGFloat(r)
        ret += UIColor.hexStringByCGFloat(g)
        ret += UIColor.hexStringByCGFloat(b)
        ret += UIColor.hexStringByCGFloat(a)
        return ret
    }
}
```

先ほどと使い方は一緒ですが、
渡す整数値はアルファ値も含めなければいけません

```swift
let headerColor = UIColor(rgba: 0x46CF3DCC)

// このように渡すと意図した色になりません
let headerColor = UIColor(rgba: 0x46CF3D)
```

ここで `CC` というアルファ値を定義していることで、0.8 程度の透過色になります


##カラーコード文字列からUIColorへの変換

今まで作ったメソッドでも充分便利ではありますが
最後にもうひとつ

たとえば、WebAPIなどの外部リソース(たとえばJSON)から色の指定が返されるなどする場合は
受け取り側であるアプリとのやりとりは文字列で行われることになると思います
それで使われるのは、HTMLカラーコードがほとんどではないでしょうか

```json
{"name":"headerColor", "color":"#46CF3D"}
```

前項までは
16進の整数値から UIColor を作る。または逆に整数値を返す機能をつけました
そして　UIColor　からカラーコードの文字列を返す機能もつけましたが
カラーコード文字列から UIColor を作るという機能は備わっていません

カラーコード文字列で相互変換できるようにしてみます

```UIColor+Utility.swift
extension UIColor {
    
    convenience init(colorCode: String) {
        self.init(rgba: UIColor.colorCodeToHex(colorCode))
    }
    
    class func colorCodeToHex(colorCode: String) -> Int {
        var colorCode = colorCode
        if colorCode.hasPrefix(self.prefix) {
            colorCode = self.substring(colorCode, location: 1)
        }
        
        switch colorCode.characters.count {
        case 8: // e.g. 35D24CFF
            break
        case 6: // e.g. 35D24C
            colorCode += "FF"
        case 4: // e.g. 35DF
            let r = self.substring(colorCode, location:0, length: 1)
            let g = self.substring(colorCode, location:1, length: 1)
            let b = self.substring(colorCode, location:2, length: 1)
            let a = self.substring(colorCode, location:3, length: 1)
            colorCode = "\(r)\(r)\(g)\(g)\(b)\(b)\(a)\(a)"
        case 3: // e.g. 35D
            let r = self.substring(colorCode, location:0, length: 1)
            let g = self.substring(colorCode, location:1, length: 1)
            let b = self.substring(colorCode, location:2, length: 1)
            colorCode = "\(r)\(r)\(g)\(g)\(b)\(b)FF"
        default: return self.minHex
        }
        
        if (colorCode as NSString).rangeOfString("^[a-fA-F0-9]+$", options: .RegularExpressionSearch).location == NSNotFound {
            return self.minHex
        }
        
        var ret: UInt32 = 0
        NSScanner(string: colorCode).scanHexInt(&ret)
        return Int(ret)
    }
    
    class func hexToColorCode(hex: Int, withPrefix prefix: Bool = true) -> String {
        var hex = hex
        if hex > self.maxHex {
           hex = self.maxHex
        }
        
        let r = CGFloat((hex & 0xFF000000) >> 24) / 255.0
        let g = CGFloat((hex & 0x00FF0000) >> 16) / 255.0
        let b = CGFloat((hex & 0x0000FF00) >>  8) / 255.0
        let a = CGFloat( hex & 0x000000FF       ) / 255.0
        
        var ret = ""
        ret += self.hexStringByCGFloat(r)
        ret += self.hexStringByCGFloat(g)
        ret += self.hexStringByCGFloat(b)
        ret += self.hexStringByCGFloat(a)
        if prefix {
            ret = self.prefix + ret
        }
        return ret
    }
}
```
ずいぶんと長い実装になりました

`colorCodeToHex` と `hexToColorCode` の両メソッドは対になっています

`hexToColorCode`でやっていることは
RGBA値から UIColor を、そしてUIColor からカラーコードを作るところでやっていたこととほぼ同じです
合わせ技で整数値からカラーコードを直接作る処理になっています


`colorCodeToHex`メソッドは
WEBのCSSまたはHTMLで使用されるカラーコードの大半を吸収できるようにしています

例えば

* "#FFFFFF"
* "#FFFFFFFF"
* "#FFF"
* "#FFFF"
* "FFFFFF"
* "FFFFFFFF"
* "FFF"
* "FFFF"

いずれの形式で渡されても
RGBAの整数値に変換できるようにしています

また、おかしな文字列が渡されるなどしてカラーコードを色として認識できない場合は
`0x0` すなわち `UIColor.clearColor` と同じ値を返すようにしています

これで

```swift
let headerColor = UIColor(rgb: 0x46CF3D)
let headerColor = UIColor(colorCode: "#46CF3D")
```
文字列からも整数からも UIColor を作ることができるようになりました


##ソースコード

今回のソースコードをまとめると以下のようになります
コピペでも使えるとは思います

```UIColor+Utility.swift
extension UIColor {
    
    var floatValues: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        return (r, g, b, a)
    }
    
    var intValues: (red: Int, green: Int, blue: Int, alpha: Int) {
        let f = self.floatValues
        return (
            UIColor.intByCGFloat(f.red),
            UIColor.intByCGFloat(f.green),
            UIColor.intByCGFloat(f.blue),
            UIColor.intByCGFloat(f.alpha)
        )
    }
}

extension UIColor {
    
    private static let maxHex = 0xFFFFFFFF
    private static let minHex = 0x0
    
    private static let prefix = "#"
    
    private class func substring(string: String, location: Int, length: Int? = nil) -> String {
        let strlen = string.characters.count
        var len = length ?? strlen
        if location + len > strlen {
            len = strlen - location
        }
        return (string as NSString).substringWithRange(NSMakeRange(location, len))
    }
    
    private class func intByCGFloat(v: CGFloat) -> Int {
        return Int(round(v * 255.0))
    }
    
    private class func hexStringByCGFloat(v: CGFloat) -> String {
        let n = self.intByCGFloat(v)
        return NSString(format: "%02X", n) as String
    }
}

extension UIColor {
    
    convenience init(rgb: Int) {
        let r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
        let g = CGFloat((rgb & 0x00FF00) >>  8) / 255.0
        let b = CGFloat( rgb & 0x0000FF       ) / 255.0
        self.init(red: r, green: g, blue: b, alpha: 1.0)
    }
    
    var rgb: Int {
        let i = self.intValues
        return (i.red * 0x010000) + (i.green * 0x000100) + i.blue
    }
    
    var rgbString: String {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        
        var ret = ""
        ret += UIColor.hexStringByCGFloat(r)
        ret += UIColor.hexStringByCGFloat(g)
        ret += UIColor.hexStringByCGFloat(b)
        return ret
    }
}

extension UIColor {
    
    convenience init(rgba: Int) {
        let r = CGFloat((rgba & 0xFF000000) >> 24) / 255.0
        let g = CGFloat((rgba & 0x00FF0000) >> 16) / 255.0
        let b = CGFloat((rgba & 0x0000FF00) >>  8) / 255.0
        let a = CGFloat( rgba & 0x000000FF       ) / 255.0
        self.init(red: r, green: g, blue: b, alpha: a)
    }
    
    var rgba: Int {
        let i = self.intValues
        return (i.red * 0x01000000) + (i.green * 0x00010000) + (i.blue *  0x00000100) + i.alpha
    }
    
    var rgbaString: String {
        var r:CGFloat = -1, g:CGFloat = -1, b:CGFloat = -1, a:CGFloat = -1
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        
        var ret = ""
        ret += UIColor.hexStringByCGFloat(r)
        ret += UIColor.hexStringByCGFloat(g)
        ret += UIColor.hexStringByCGFloat(b)
        ret += UIColor.hexStringByCGFloat(a)
        return ret
    }
}

extension UIColor {
    
    convenience init(colorCode: String) {
        self.init(rgba: UIColor.colorCodeToHex(colorCode))
    }
    
    class func colorCodeToHex(colorCode: String) -> Int {
        var colorCode = colorCode
        if colorCode.hasPrefix(self.prefix) {
            colorCode = self.substring(colorCode, location: 1)
        }
        
        switch colorCode.characters.count {
        case 8: // e.g. 35D24CFF
            break
        case 6: // e.g. 35D24C
            colorCode += "FF"
        case 4: // e.g. 35DF
            let r = self.substring(colorCode, location:0, length: 1)
            let g = self.substring(colorCode, location:1, length: 1)
            let b = self.substring(colorCode, location:2, length: 1)
            let a = self.substring(colorCode, location:3, length: 1)
            colorCode = "\(r)\(r)\(g)\(g)\(b)\(b)\(a)\(a)"
        case 3: // e.g. 35D
            let r = self.substring(colorCode, location:0, length: 1)
            let g = self.substring(colorCode, location:1, length: 1)
            let b = self.substring(colorCode, location:2, length: 1)
            colorCode = "\(r)\(r)\(g)\(g)\(b)\(b)FF"
        default: return self.minHex
        }
        
        if (colorCode as NSString).rangeOfString("^[a-fA-F0-9]+$", options: .RegularExpressionSearch).location == NSNotFound {
            return self.minHex
        }
        
        var ret: UInt32 = 0
        NSScanner(string: colorCode).scanHexInt(&ret)
        return Int(ret)
    }
    
    class func hexToColorCode(hex: Int, withPrefix prefix: Bool = true) -> String {
        var hex = hex
        if hex > self.maxHex {
           hex = self.maxHex
        }
        
        let r = CGFloat((hex & 0xFF000000) >> 24) / 255.0
        let g = CGFloat((hex & 0x00FF0000) >> 16) / 255.0
        let b = CGFloat((hex & 0x0000FF00) >>  8) / 255.0
        let a = CGFloat( hex & 0x000000FF       ) / 255.0
        
        var ret = ""
        ret += self.hexStringByCGFloat(r)
        ret += self.hexStringByCGFloat(g)
        ret += self.hexStringByCGFloat(b)
        ret += self.hexStringByCGFloat(a)
        if prefix {
            ret = self.prefix + ret
        }
        return ret
    }
}
```