I currently have following custom component that wraps and styles a default TextField
from swift ui. I came across an issue where I now need to re-use this where @Binding
is no longer just String, but rather, can be a number like int or double, thus underlying TextField
needs to change and take in TextField(value: ..., formatter: ...)
instead of just text
.
I can turn all of the modifiers into a custom modifier and apply it to relevant text fields to apply the styling, but am wondering if there is a solution where I can keep this as a custom component and instead allow TextInputSmall
to take in and pass down all possible permutations of TextField arguments, for example
TextInputSmall("", text: $someString)
and TextInputSmall("", value: $someNumber, formatter: .number)
struct TextInputSmall: View {
// Public Variables
var accentColor = Color._orange
@Binding var text: String
// Private Variables
@FocusState private var focused: Bool
// Body
var body: some View {
TextField(
"",
text: $text
)
.font(...)
.foregroundStyle(...)
.accentColor(...)
.focused($focused)
.submitLabel(.done)
.frame(...)
.padding(...)
.background(...)
}
}
2
Answers
You can basically "steal" the declarations of
TextField
and its initialisers and put them into your own code. Add aTextField
property in your wrapper, and in each initialiser, initialise that property by creating aTextField
using the corresponding initialiser.Here is an example for
init(_:text:)
andinit(_:value:format)
:That said, this is quite tedious if you want to have all the combinations of
TextField.init
. IMO, using aViewModifier
like you suggested is the more idiomatic and correct solution.As already mentioned, the better solution would be to use a ViewModifier.
But, you need to be clear what the modifier is responsible for and for what not.
You could think to implement it like:
I commented out those properties, which should not go into this modifier. This has a clear reason. When you look closer, the modifier only mutates "style" properties, but no "layout" properties.
It also does not set the focus, which is completely orthogonal what a style modifier should do.
So, when designing a modifier, try to separate the concepts. Don’t mix up style with layout, and don’t handle state properties (like focus) in the modifier.
Regarding the focus, you should provide a FocusState in a parent view where it has several views having a focus. Putting it into a custom view, which wraps a single TextField makes no sense.
Once you have the modifier, you don’t need the custom view, just apply the modifier to any view.