UIColorを、#000000 のような 16進数(Hex String)に変換する方法。ググるといくつか出てくると思う。以下のようなやつ。
func toHexString1(_ color:UIColor) -> String? { | |
guard let components = color.cgColor.components else { return nil } | |
return toHexString(components) | |
} | |
func toHexString2(_ color:UIColor) -> String? { | |
var r: CGFloat = 0 | |
var g: CGFloat = 0 | |
var b: CGFloat = 0 | |
var a: CGFloat = 0 | |
guard color.getRed(&r, green: &g, blue: &b, alpha: &a) else { return nil } | |
return toHexString([r, g, b, a]) | |
} | |
func toHexString3(_ color:UIColor) -> String? { | |
let ciColor = CIColor(cgColor: color.cgColor) | |
return toHexString([ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]) | |
} | |
func toHexString(_ components:[CGFloat]) -> String? { | |
if components.count < 3 { return nil } | |
let r = Float(components[0]) | |
let g = Float(components[1]) | |
let b = Float(components[2]) | |
let a = components.count >= 4 ? Float(components[3]) : 1.0 | |
if a != 1 { | |
return String(format: "#%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255)) | |
} else { | |
return String(format: "#%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255)) | |
} | |
} |
toHexString1() は、cgColorのcomponentを使う方法。
toHexString2() は、getRed で取得する方法。
toHexString3() は、CIColor 経由で取得する方法。
コレらをそのまま使うと、変換に失敗する場合もある。
注目すべきは、UIColor が どの Color Spaceが使われているか?
Display P3などは、sRGBよりも大きな範囲が対象になるので、、Extended sRGBに変換すると0.0 から 1.0 に収まらない値、つまり、マイナスや、1.0 より大きい数字になる場合がある。
Display P3 で r, g, b = 1.0, 0.0, 0.0 は、Extended sRGB で、r, g, b = 1.093, -0.227, 0.15 になる。
(使用しているiOSのバージョンによって差異あるかも)
「1. CGColorSpace.displayP3 で作成」と、「2. displayP3Red で作成」は、ともにDisplay P3 の値を指定して作成する方法だが、上記のように構成は変わるみたい。
「2. displayP3Red で作成」の時のように、UIColorをprintで表示した情報と、cgColorの情報は一致しない場合もある。
UIColorPickerViewControllerで取得できる色は、「1. CGColorSpace.displayP3 で作成」の方の構成。
UIColorPickerViewControllerで、Color Spaceを指定できれば良いのになぁ。
また、GrayScaleの場合、componentsの数は2つになることも注意。
安全に Hex Stringを作成するためには、sRGB (or Display P3) に変換してから作成するのが良さそう。
func toHexString4(_ color:UIColor) -> String? { | |
var cgColor:CGColor = color.cgColor | |
if let name = cgColor.colorSpace?.name, name != CGColorSpace.sRGB { | |
cgColor = color.cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)! | |
} | |
let ciColor = CIColor(cgColor: cgColor) | |
return toHexString([ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]) | |
} |
UIColor をアプリに保存する
おまけ
extension UIColor { | |
convenience init?(hexString: String) { | |
let hexString = hexString.trimmingCharacters(in: .whitespacesAndNewlines) | |
let scanner = Scanner(string: hexString) | |
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#") | |
var color: UInt64 = 0 | |
if scanner.scanHexInt64(&color) { | |
self.init(hex: color, useOpacity: hexString.count > 7) | |
} else { | |
return nil | |
} | |
} | |
convenience init(hex: UInt64, useOpacity: Bool = false) { | |
let mask = UInt64(0xFF) | |
let cap = !useOpacity && hex > 0xffffff ? 0xffffff : hex | |
let r = cap >> (useOpacity ? 24 : 16) & mask | |
let g = cap >> (useOpacity ? 16 : 8) & mask | |
let b = cap >> (useOpacity ? 8 : 0) & mask | |
let a = useOpacity ? cap & mask : 255 | |
self.init(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(a) / 255.0) | |
} | |
func toHexString() -> String { | |
var target = self.cgColor | |
if let name = cgColor.colorSpace?.name, name != CGColorSpace.sRGB { | |
let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)! | |
if let sRGBColor = cgColor.converted(to: colorSpace, intent: .defaultIntent, options: nil) { | |
target = sRGBColor | |
} | |
} | |
let ciColor = CIColor(cgColor: target) | |
let r = lroundf(Float(ciColor.red) * 255) | |
let g = lroundf(Float(ciColor.green) * 255) | |
let b = lroundf(Float(ciColor.blue) * 255) | |
let a = lroundf(Float(ciColor.alpha) * 255) | |
if a != 255 { | |
return String(format: "#%02lX%02lX%02lX%02lX", r, g, b, a) | |
} else { | |
return String(format: "#%02lX%02lX%02lX", r, g, b) | |
} | |
} | |
} |