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)
}
}
}