ottijp blog

SwiftUIを使う際にhex(16進表記)文字列でColorを作成する方法

  • 2023-12-17

これは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"))
  }
}

ユーザ入力をパースする場合はもう少し考えたほうがよさそうですが,内部的に使う分にはこのくらいで十分かなと考えています.


ottijp
都内でアプリケーションエンジニアをしています
© 2024, ottijp