アプリ開発で色情報を管理する

f:id:daihase:20190809100049p:plain

こんにちは、daihaseです。今日はSwiftネタを。 アプリ開発をしていて、背景の色だったりタイトルの色だったり、皆さまこの辺どんな風に管理していますでしょうか。

例えば背景の色を変える、といった場合デフォルトだと以下のやり方になりますね。

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

 

初期の頃はかくいう私もこういうのを各ファイルにとにかく直書きしていました。 ただ、ある程度規模の大きいアプリとかになると当然同じ色を毎回宣言することになったりして、それが変更になった場合関連するファイル全てに修正が入るなど相当手間です。

と、まぁ普通に仕事で開発をされてる方なんかだとこの辺ベタで記述してるところなんか絶対ないとは思いますが、個人とか趣味でやられてる方はうまいまとめ方がわからないという方もいるかなと思い、今回1例を紹介してみます。

 

例えばSketchPhotoshopなどのツールでアプリのワイヤーなどを作成していて、デザイナーさんが16進数で色情報を明記してくれることは多いかと思います。

アプリでもその情報をそのまま活用出来るといいですよね。

というわけで、Swiftでは上記例のようにRed、Blue、Greenと数値を指定する方法があるのですが16進数を指定して色を取得する方法がないため、それを用意してやります。

 

import Foundation
import UIKit

public extension UIColor {
    convenience init(hex: Int, alpha: CGFloat) {
        let r = CGFloat((hex & 0xFF0000) >> 16) / 255.0
        let g = CGFloat((hex & 0x00FF00) >> 8) / 255.0
        let b = CGFloat(hex & 0x0000FF) / 255.0
        self.init(red: r, green: g, blue: b, alpha: alpha)
    }
    convenience init(hexString str: String, alpha: CGFloat) {
        let range = NSMakeRange(0, str.count)
        let hex = (str as NSString).replacingOccurrences(of: "[^0-9a-fA-F]", with: "", options: NSString.CompareOptions.regularExpression, range: range)
        var color: UInt32 = 0
        Scanner(string: hex).scanHexInt32(&color)
        self.init(hex: Int(color), alpha: alpha)
    }
    var hexString: String? {
        return self.cgColor.hexString
    }
    var RGBa: (red: Int, green: Int, blue: Int, alpha: CGFloat)? {
        return self.cgColor.RGBa
    }
}

extension CGColor {
    var hexString: String? {
        if let x = self.RGBa {
            let hex = x.red * 0x10000 + x.green * 0x100 + x.blue
            return NSString(format:"%06x", hex) as String
        } else {
            return nil
        }
    }
    var RGBa: (red: Int, green: Int, blue: Int, alpha: CGFloat)? {
        let colorSpace = self.colorSpace
        let colorSpaceModel = colorSpace?.model
        if colorSpaceModel?.rawValue == 1 {
            let x = self.components
            let r: Int = Int(x![0] * 255.0)
            let g: Int = Int(x![1] * 255.0)
            let b: Int = Int(x![2] * 255.0)
            let a: CGFloat = x![3]
            return (r, g, b, a)
        } else {
            return nil
        }
    }
}

 

ここではUIColor+Ex.swiftといったファイルを新たに作成します。

細かい説明は省きますがhexStringというのがいわば16進数を表す文字列で、これを元にUIColorを拡張したイニシャライザを用いて色情報を生成するイメージです。

 

このファイルがあれば、view.backgroundColor = UIColor(hexString: "#FFFFFF", alpha: 1.0) などと記述してやれば、そのまま16進数指定で色情報をセット出来るのですが、これだとまだ各ファイルにベタ書きになってしまいますね。

 

プロジェクト内で色情報を一元管理するためのファイルを作成しましょう。

import UIKit

final class ColorPalette: NSObject {
    class func mainBackgroundColor() -> UIColor {
        return UIColor(hexString: "#0000FF", alpha: 1.0)
    }

    class func boxColor() -> UIColor {
        return UIColor(hexString: "#FF0000", alpha: 1.0)
    }
}

 

boxColor()だったら箱の色を担当するメソッドですね。mainBackgroundColor()は背景色を担当。こんな感じにこのファイルにどんどん色情報を追記していく形になります。

ではViewController.swiftで実際に呼び出してセットしてみましょう。

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // 背景の色をセット
        view.backgroundColor = ColorPalette.mainBackgroundColor()

        // box生成
        let box: UIView = {
            let v = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
            // boxの色をセット
            v.backgroundColor = ColorPalette.boxColor()
            return v
        }()

        view.addSubview(box)
        box.center = view.center
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

 

これなら例えば現場で後から「やっぱり背景は黄色にして」と言われても色情報を管理しているColorPalette.swiftを見にいってそこの16進数を変更してやれば、セットしてる箇所も一斉にかわるので変更に強い設計と言えるんではないでしょうか。

 

複数人で開発してる現場なんかでは、この色情報に限らずこうした設計は当たり前のように行われているので、個人で開発するような小規模なものでも最初からこうした管理用のクラスは用意しておけば後々開発も楽になってくるので良いかと思います。

それでは良い開発ライフを〜