Home > database >  How to check if a specific color is selected with SwiftUI's ColorPicker?
How to check if a specific color is selected with SwiftUI's ColorPicker?

Time:06-21

I have a ColorPicker in my SwiftUI app that allows me to select the color of some shapes in a Canvas view. When black is selected in light mode or white is selected in dark mode I want to replace the selected color with SwiftUI's dynamic Color.primary value so that when I change the color theme the foreground color is automatically inverted. Otherwise the shapes would turn invisible in front of the same colored background.

The color selected with the ColorPicker view is saved as a standard SwiftUI Color. Nonetheless I could not figure out a way to check if black or white is selected. It seems like the ColorPicker creates the SwiftUI colors through UIColor and CGColor somehow. It could also have something to do with mismatching color spaces I guess.

I tried it in several different ways but couldn't get it to work. Maybe I'm stupid and there's an obvious solution but I just couldn't find it. Thanks in advance for any help!

Here is what I tried (the ColorPicker has a binding to the baseColor property):

@Published var baseColor: Color {
    didSet {
        print("Hello world!") // prints as expected when setting the color

        if baseColor == Color.black {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        if baseColor == Color(red: 0, green: 0, blue: 0) {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        if baseColor == Color(red: 0, green: 0, blue: 0, opacity: 1) {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        if baseColor == Color(white: 0) {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        if baseColor == Color(.displayP3, red: 0, green: 0, blue: 0, opacity: 1) {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        if baseColor == Color(.sRGB, red: 0, green: 0, blue: 0, opacity: 1) {
            baseColor = .primary
            print("Base color changed!") // not called when color is set to black
        }

        ...
    }
}

When I print baseColor to the console I get "kCGColorSpaceModelRGB 0 0 0 1".

CodePudding user response:

Try using

.onChange(of: color) { _ in
  
   // Conditions when color changes

}

CodePudding user response:

As I mentioned, the main issue is comparing two Color. So try this approach to compare the Color you select with black and white that you want. Adjust the approach to your needs.

// from: https://stackoverflow.com/questions/56586055/how-to-get-rgb-components-from-color-in-swiftui
extension Color {
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, opacity: CGFloat) {
        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var o: CGFloat = 0
        guard UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &o) else {
            return (0, 0, 0, 0)
        }
        return (r, g, b, o)
    }
    
    // very strict
    static func == (lhs: Color, rhs: Color) -> Bool {
        lhs.components.red == rhs.components.red &&
        lhs.components.green == rhs.components.green &&
        lhs.components.blue == rhs.components.blue &&
        lhs.components.opacity == rhs.components.opacity
    }
    
    // about similar?
    static func === (lhs: Color, rhs: Color) -> Bool {
        let d = 0.01  // <-- here adjust to your needs
        return lhs.components.red >= rhs.components.red - d && lhs.components.red <= rhs.components.red   d &&
        lhs.components.green >= rhs.components.green - d && lhs.components.green <= rhs.components.green   d &&
        lhs.components.blue >= rhs.components.blue - d && lhs.components.blue <= rhs.components.blue   d &&
        lhs.components.opacity >= rhs.components.opacity - d && lhs.components.opacity <= rhs.components.opacity   d
    }
}

class ColorModel: ObservableObject {
    
    @Published var baseColor: Color {
        didSet {
            // change to == and see the difference
            if baseColor === Color.black || baseColor === Color.white {
                baseColor = .primary
            }
        }
    }
    
    init() {
        self.baseColor = Color.orange  // for testing
    }
}

struct ContentView: View {
    @StateObject var model = ColorModel()
    
    var body: some View {
        VStack {
            Text(model.baseColor.description)  // <-- to show it works
            ColorPicker("colors", selection: $model.baseColor)
        }
    }
}
  • Related