痛点
在使用SwiftUI时,可能需要将Color转换为JSON以便存储、传输,然而Color本身又难以get数值属性。
虽然可以写一个只包含rgb的颜色类,但是如果使用了ColorPicker这样的View又只能使用Color或者CGColor来实现,因此在重写ColorPicker和重写Color之间我选择重写Color。
实现目标
- Codable是最起码的
- 和Color或者CGColor的互转
- 在ColorPicker中大概能使用
SwiftUI中的Color和UIColor
Color
Color类虽然非常直观易用,内置了很多颜色,但是持久化存储挺麻烦的,好在可以和CGColor互转,于是就开始研究CGColor的存储
CGColor
相对Color而言,CGColor就看起来容易存储许多,不过有一点麻烦的是色彩空间的指定,所以我的实现里面只有sRGB和p3,其他色彩空间同理。
MyColor的实现
属性
var r: CGFloat
var g: CGFloat
var b: CGFloat
var alpha: CGFloat
var space : Space = .srgb
enum Space: String, Codable {
case srgb
case p3
}
和Color的转换
// 使用相似的方法创建
init(r:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat) {
self.r = r
self.g = g
self.b = b
self.alpha = a
}
func toColor() -> Color {
return Color(toCGColor()).opacity(alpha)
}
和CGColor的转换
init(cgcolor: CGColor) {
let colorSpaceName : CGColorSpace = cgcolor.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
let colorComponents = cgcolor.components!
if colorComponents.count == 4 {
r = colorComponents[0]
g = colorComponents[1]
b = colorComponents[2]
alpha = colorComponents[3]
} else {
fatalError("cant convert cgcolor to mycolor")
}
switch colorSpaceName {
case CGColorSpace(name: CGColorSpace.sRGB): space = Space.srgb
case CGColorSpace(name: CGColorSpace.displayP3): space = Space.p3
default:space = Space.srgb
}
}
func toCGColor() -> CGColor {
return CoreGraphics.CGColor(colorSpace: colorSpace, components: [r,g,b,alpha])!
}
使用示例
import SwiftUI
struct ColorPickerWithMyColor: View {
@Binding var color: MyColor
@State var colorTemp : CGColor = .random()
var body: some View {
HStack {
ColorPicker(selection: $colorTemp){
// let c = Color.random()
}
.onChange(of: colorTemp, perform: { newColor in
color = MyColor(cgcolor: newColor)
})
}
.onAppear(perform: {
colorTemp = color.toCGColor()
})
}
}
MyColor的完整代码
import SwiftUI
class MyColor: Codable {
var r: CGFloat
var g: CGFloat
var b: CGFloat
var alpha: CGFloat
var space : Space = .srgb
enum CodingKeys: String, CodingKey {
case r,g,b,alpha,space
}
enum Space: String, Codable {
case srgb
case p3
}
init(r:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat) {
self.r = r
self.g = g
self.b = b
self.alpha = a
}
init(cgcolor: CGColor) {
let colorSpaceName : CGColorSpace = cgcolor.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
let colorComponents = cgcolor.components!
if colorComponents.count == 4 {
r = colorComponents[0]
g = colorComponents[1]
b = colorComponents[2]
alpha = colorComponents[3]
} else {
fatalError("cant convert cgcolor to mycolor")
}
switch colorSpaceName {
case CGColorSpace(name: CGColorSpace.sRGB): space = Space.srgb
case CGColorSpace(name: CGColorSpace.displayP3): space = Space.p3
default:space = Space.srgb
}
}
func toCGColor() -> CGColor {
return CoreGraphics.CGColor(colorSpace: colorSpace, components: [r,g,b,alpha])!
}
func toColor() -> Color {
return Color(toCGColor()).opacity(alpha)
}
var colorSpace: CGColorSpace {
switch space {
case .srgb : return CGColorSpace(name: CGColorSpace.sRGB)!
case .p3 : return CGColorSpace(name: CGColorSpace.displayP3)!
}
}
static func random() -> MyColor {
return MyColor(r: .random(in: 0...1), g: .random(in: 0...1), b: .random(in: 0...1), a: .random(in: 0.5...1))
}
static func random() -> CGColor {
return MyColor.random().toCGColor()
}
}
extension MyColor: Equatable {
static func == (lop: MyColor, rop: MyColor) -> Bool {
return lop.toCGColor() == rop.toCGColor()
}
}