SwiftUIを使う際にhex(16進表記)文字列でColorを作成する方法
これはInfocom Advent Calendar 2023 17日目の記事です.
SwiftUIのColor型は,red/green/blue/opacityを引数に取るコンストラクタなどは用意されていますが,hex文字列(#112233
のような形式)を引数に取るコンストラクタは用意されていません.
そこで,文字列でhexを受け取るコンストラクタを作ってみました.
基本はこちらを参考にしてアレンジしました.
cf. [Swift] UIColorと16進数(Hex)カラーコードを双方向変換する | Mulong.me
環境
- macOS: 13.5 (Ventura)
- Xcode: 15.0
- Swift: 5.9
- iOS: 17.0
コード
import Foundation
import SwiftUI
extension Color {
/// create new object with hex string
init?(hex: String, opacity: Double = 1.0) {
// delete "#" prefix
let hexNorm = hex.hasPrefix("#") ? String(hex.dropFirst(1)) : hex
// scan each byte of RGB respectively
let scanner = Scanner(string: hexNorm)
var color: UInt64 = 0
if scanner.scanHexInt64(&color) {
let red = CGFloat((color & 0xFF0000) >> 16) / 255.0
let green = CGFloat((color & 0x00FF00) >> 8) / 255.0
let blue = CGFloat(color & 0x0000FF) / 255.0
self.init(red: red, green: green, blue: blue, opacity: opacity)
} else {
// invalid format
return nil
}
}
}
テストコード
import XCTest
import SwiftUI
@testable import SwiftUIHexColorLib
final class UIColorHexCodeTests: XCTestCase {
func testWhite() throws {
let color = Color(hex: "FFFFFF")!
XCTAssertEqual(color, Color(red: 1.0, green: 1.0, blue: 1.0))
}
func testBlack() throws {
let color = Color(hex: "000000")!
XCTAssertEqual(color, Color(red: 0.0, green: 0.0, blue: 0.0))
}
func testSkyBlue() throws {
let color = Color(hex: "#3366cc", opacity: 0.9)!
XCTAssertEqual(color, Color(red: 0.2, green: 0.4, blue: 0.8, opacity: 0.9))
}
func testInvalidFormat() throws {
XCTAssertNil(Color(hex: "white"))
}
}
ユーザ入力をパースする場合はもう少し考えたほうがよさそうですが,内部的に使う分にはこのくらいで十分かなと考えています.