Home > Mobile >  Creating a Binding to a computed var called Color for ColorPicker
Creating a Binding to a computed var called Color for ColorPicker

Time:08-12

So basically I have an array of Themes and one attribute of a Theme is RGBAColor, which is basically 4 doubles. I'm intentionally not storing a Color struct because I don't want UI stuff like Colors to be in my model, which is UI independent. In my ViewModel, I "interpret" this RGBAColor to a normal Color struct.

I want the user to be able to change the color attribute of a theme through color picker, but since colorpicker takes in a binding to a color and not an RGBAColor, I'm in a bit of trouble. I was told that a hint is to create a binding to a computed var, so I made one

extension Theme {

    var color: Binding<Color> {
        Binding {
            return Color(rgbaColor: self.rgbaColor)
        } set: { newRGBAColor in
            self.rgbaColor = RGBAColor(color: newRGBAColor)
        }
    }
}

but this doesn't work because self is immutable. How can I fix this?

struct ThemeEditor: View {
    @Binding var theme: Theme

    var colorSection: some View {
        Section(header: Text("Colors")) {
            VStack {
                ColorPicker("Selection", selection: $theme.color)
            }
        }
    }
}
struct RGBAColor: Codable, Equatable, Hashable {
    let red: Double
    let green: Double
    let blue: Double
    let alpha: Double
}
extension RGBAColor {
    init(color: Color) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        if let cgColor = color.cgColor {
            UIColor(cgColor: cgColor).getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        }
        self.init(red: Double(red), green: Double(green), blue: Double(blue), alpha: Double(alpha))
    }
    
    init(_ red: Double, _ green: Double, _ blue: Double, _ alpha: Double) {
        self.init(red: red/255, green: green/255 , blue: blue/255, alpha: alpha)
    }
}

extension Color {
    init(rgbaColor rgba: RGBAColor) {
        self.init(.sRGB, red: rgba.red, green: rgba.green, blue: rgba.blue, opacity: rgba.alpha)
    }
}

struct Theme: Identifiable, Codable, Hashable {
    var name: String
    var emojis: String
    var id: Int
    var numberOfPairsOfCards: Int
    var rgbaColor: RGBAColor
    
    fileprivate init(name: String, emojis: String, id: Int, numberOfPairsOfCards: Int, rgbaColor: RGBAColor) {
        self.name = name
        self.emojis = emojis
        self.id = id
        self.numberOfPairsOfCards = numberOfPairsOfCards
        self.rgbaColor = rgbaColor
    }
}

CodePudding user response:

You just need to put it in view, like

struct ThemeEditor: View {
    @Binding var theme: Theme

    var color: Binding<Color> {     // << here !!
        Binding {
            return Color(rgbaColor: theme.rgbaColor)
        } set: { newRGBAColor in
            theme.rgbaColor = RGBAColor(color: newRGBAColor)
        }
    }

    var colorSection: some View {
        Section(header: Text("Colors")) {
            VStack {
                ColorPicker("Selection", selection: self.color) // << here !!
            }
        }
    }

    // ...
}

Tested with Xcode 13.4

  • Related