I’m trying to make reusable custom views that themselves can be customized later. Ideally I’d be able to define default modifiers like Color, Font, etc that the Views would have without any customization, but allow these to be easily overwritten by additional modifiers used later on.
For example if I had the custom view:
struct MyCustomTextField: View {
@Binding var text: String
var body: some View {
HStack {
Image(systemName: "envelope").foregroundColor(.gray)
TextField("", text: $text)
.foregroundColor(.gray)
.font(.system(.title))
}
}
}
The view would have default gray foreground color and the TextField would have title font. But now if I wanted to reuse this view and customize it for my specific use-case, I might want to override these modifiers like so:
struct ContentView: View {
@State var text = "hello"
var body: some View {
MyCustomTextField(text: $text)
.foregroundColor(.blue)
.font(.system(.body))
}
}
But these outside modifiers are not effective as the inner modifiers take precedent.
What’s the best way to make custom views so that I can define default modifiers for their contents but still be able to override those defaults later?
Thanks
2
Answers
We should always remember that SwiftUI Views are value types, therefore you cannot change them after the initialization like you would do on UIKit.
One possible way would be to inject it on the initialization.
But if you wanna make it adjustable after the initialization (e.g to change the color upon pressing some other button), use the binding mechanism:
Add a
@Binding
to your MyCustomTextField, and use its value at the foregroundColor modifier.and then in the client view you can do as following:
now, everytime you will click the button, it will toggle hasSpecialColor (which is
@State
), and therefore the view will render and the value for the@Binding
foregroundColor will change accordingly.The
font
modifier stores theFont
in the environment for access by the modified views. You can read it and provide a default like this:Unfortunately, the
foregroundColor
modifier doesn’t store its setting in a (public) environment property, so you have to go a different route. One way is to provide a modifier directly on your custom view, like this:You can then use the
myColor
modifier directly on aMyCustomTextField
like this:But you cannot use it on any enclosing view or after any non-
MyCustomTextField
-specific modifier. For example, this will not work:If you want that to work, then you need to store the custom color in the environment, like this:
And then you can use the
myColor
modifier on any view, and it will apply to all enclosed subviews: