I wanted my Custom views to behave like SwiftUI Views and for simplicity I'm considering the background and foreground colors. Now, instead of individually defining the foreground/background color of each subview of a view, I want to change it externally as in SwiftUI views. For simplicity, there is one subView: Text; on my tries.
import SwiftUI
struct TestButton: View {
var text: String
@State private var foregroundColor: Color = .primary
@State private var backgroundColor: Color = .white
@State private var id = UUID()
var body: some View {
Text(text)
.foregroundColor(foregroundColor )
.padding()
.background(backgroundColor)
.cornerRadius(10)
.id(id)
.accentColor(foregroundColor)
}
public func foregroundColor(_ color: Color) {
foregroundColor = color
id = UUID()
}
public func backgroundFill(_ color: Color) {
backgroundColor = color
id = UUID()
}
}
struct TestButton_Previews: PreviewProvider {
static var previews: some View {
TestButton(text: "Test")
.foregroundColor(.red)
.backgroundFill(.green)
}
}
This doesn't work on Xcode 14.3 canvas, although I was expecting to work. So, I've turned into emulator to see there is a difference and the result was same as in canvas.
How can we design a Custom View so that we can use color changes as in SwiftUI's View?
Your question is very interesting because of what is fairly straight-forward and what is impossible, and the very surprising dividing line between them. I'll build this up from absolutely trivial to impossible.
First, start with mapping
.foregroundColor()to the color of the text. That is trivial and in fact requires no code at all. It "just works."There's no need for any variables. The value of
.foregroundColor()is passed directly to Text with no intervention at all. Magic!How do you recreate that magic for
backgroundFill? With Environment. This is how these settings are passed down the hierarchy. First, create an environment key, and extend EnvironmentValues to access it:Then, use
@Environmentto access it:For convenience, add an extension to View to set it:
And then use it:
And this is how SwiftUI manages its own Views.
But I've left out one bit of your question, which is this modifier:
In your example, this doesn't actually matter. Text doesn't use
accentColorthis way. But I think you're asking a very general question, which is "how do I setaccentColorto beforegroundColor?" And a little more broadly, "how do I accessforegroundColor?"The answer is you don't. That environment value is marked internal. I don't know why. Here is some useful commentary: How To Read Foreground and Tint Colors in SwiftUI Views, and a possible workaround/hack. But ultimately, it's not publicly available.
You can, of course, create your own
.foregroundTextButtonColorfollowing the above explanation of.backgroundFill, but it won't integrate with SwiftUI's.foregroundColor.