web-dev-qa-db-ja.com

UIColorをコード化可能にする

struct Task: Codable {
    var content: String
    var deadline: Date
    var color: UIColor
...
}

「タイプ「タスク」はプロトコル「デコード可能」に準拠していません」および「タイプ「タスク」はプロトコル「エンコード可能」に準拠していません」という警告があります。私が検索したところ、UIColorがCodableに準拠していないことが原因であることがわかりました。しかし、私はそれを修正する方法を知りません。そう...

UIColorをコード化する方法は?

12
Lambdalex

4色のコンポーネントのみを気にする場合、これはラッパー構造体を使用した簡単なソリューションです。

struct Color : Codable {
    var red : CGFloat = 0.0, green: CGFloat = 0.0, blue: CGFloat = 0.0, alpha: CGFloat = 0.0

    var uiColor : UIColor {
        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }

    init(uiColor : UIColor) {
        uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
    }
}

この場合、4つのカラーコンポーネントをColorからUIColorに、またはその逆に変換するカスタム初期化子を作成する必要があります。

struct Task: Codable {

    private enum CodingKeys: String, CodingKey { case content, deadline, color }

    var content: String
    var deadline: Date
    var color : UIColor

    init(content: String, deadline: Date, color : UIColor) {
        self.content = content
        self.deadline = deadline
        self.color = color
    }

   init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        content = try container.decode(String.self, forKey: .content)
        deadline = try container.decode(Date.self, forKey: .deadline)
        color = try container.decode(Color.self, forKey: .color).uiColor
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(content, forKey: .content)
        try container.encode(deadline, forKey: .deadline)
        try container.encode(Color(uiColor: color), forKey: .color)
    }
}

これでUIColorをエンコードおよびデコードできます

let task = Task(content: "Foo", deadline: Date(), color: .orange)
do {
    let data = try JSONEncoder().encode(task)
    print(String(data: data, encoding: .utf8)!)
    let newTask = try JSONDecoder().decode(Task.self, from: data)
    print(newTask)
} catch {  print(error) }
13
vadian

UIColorサブクラスを使用しています

final class Color: UIColor, Decodable {
    convenience init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let hexString = try container.decode(String.self)
        self.init(hex: hexString)
    }
}

したがって、Decodableプロトコルの機能を実装するために、各クラスまたは構造が必要になることはありません。これは、特に1つのクラスまたは構造に多くのカラーパラメータが存在する可能性がある場合に、最も便利な方法であるように思われます。必要に応じて、同じ方法でEncodableを実装できます。

3
Nikaaner

この問題は、codableへの自動適合を可能にするカスタムクラスで解決しました。これは、カスタム適合をcodableに書き込むのを防ぐため、有益です。また、UIColorおよびCGColorでの作業が容易になります

class Color:Codable{

private var _green:CGFloat
private var _blue:CGFloat
private var _red:CGFloat
private var alpha:CGFloat

init(color:UIColor) {
    color.getRed(&_red, green: &_green, blue: &_blue, alpha: &alpha)
}

var color:UIColor{
    get{
        return UIColor(red: _red, green: _green, blue: _blue, alpha: alpha)
    }
    set{
        newValue.getRed(&_red, green:&_green, blue: &_blue, alpha:&alpha)
    }
}

var cgColor:CGColor{
    get{
        return color.cgColor
    }
    set{
        UIColor(cgColor: newValue).getRed(&_red, green:&_green, blue: &_blue, alpha:&alpha)
    }
}

}

2
Shadrach Mensah

以下は、任意の色空間の任意の色で機能するソリューションです。

/// Allows you to use Swift encoders and decoders to process UIColor
public struct CodableColor {

    /// The color to be (en/de)coded
    let color: UIColor
}



extension CodableColor: Encodable {

    public func encode(to encoder: Encoder) throws {
        let nsCoder = NSKeyedArchiver(requiringSecureCoding: true)
        color.encode(with: nsCoder)
        var container = encoder.unkeyedContainer()
        try container.encode(nsCoder.encodedData)
    }
}



extension CodableColor: Decodable {

    public init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        let decodedData = try container.decode(Data.self)
        let nsCoder = try NSKeyedUnarchiver(forReadingFrom: decodedData)
        self.color = try UIColor(coder: nsCoder).unwrappedOrThrow()
    }
}

使い方は比較的簡単です。

let color = UIColor.label

let encoder = JSONEncoder()
let encodedData = try encoder.encode(color.codable())

let decoder = JSONDecoder()
let decodedColor = try decoder.decode(CodableColor.self, from: encodedData).color

もちろん、他の任意のSwift codableとしても使用できます。自動合成されたコード化適合性を持つ構造体のように:

struct Foo: Codable {
    let color: CodableColor

    init(color: UIColor) {
        self.color = CodableColor(color: color)
    }
}
let fooInstance = Foo(color: .systemPurple)

let encoder = JSONEncoder()
let encodedData = try encoder.encode(fooInstance)

let decoder = JSONDecoder()
let decodedFoo = try decoder.decode(Foo.self, from: encodedData)

これはNSColorでも機能します。

2
Ben Leggiero