Reputation: 17251
I have a very simple SwiftUI view that only shows a TextField
. The text field's text
is bound to the string
property of my viewModel
that I instantiate as a @StateObject
:
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
TextField("Placeholder", text: $viewModel.string)
.padding()
}
}
The one thing that's "out of the ordinary" is that my string
property is not a @Published
property, but a simple computed property. In its setter, it sets the displayedString
property to a fixed string (for testing purposes) which is a @Published property:
class ViewModel: ObservableObject {
@Published var displayedString: String = ""
var string: String {
get { displayedString }
set { displayedString = "OVERRIDE" }
}
}
So when I type a string into the text field, I would expect the setter to trigger a view update (as it updates a @Published
property) and then in the view, the text field should be updated with the displayedString
. So in other words: No matter what I type, the text field should always show the string OVERRIDE
.
But that's not the case!
It works for the very first letter I type, but then I can freely type anything into the text field.
For example, if I launch the app with only this view and type the string
123456789
this is what is displayed on the text field:
OVERRIDE23456789
And how can I get the text field to always be up-to-date to what's set on the viewModel
? (I know, I can just hook the text field to a @Published
property directly, but I'm doing this computed property stuff for a reason and want to understand why it doesn't work as expected.)
When I add a Text
label above the TextField
and just make it show the viewModel.string
, it shows the correct (expected) string:
var body: some View {
Text(viewModel.string)
TextField("Placeholder", text: $viewModel.string)
.padding()
}
Upvotes: 3
Views: 1808
Reputation: 222
I’m not sure but I think a custom binding might work for you
In the ViewModel use
var string: Binding<String> { Binding(
get: { displayedString },
set: { displayedString = “OVERRIDE” } )}
Then in body use: viewModel.string instead of $viewModel.string in the TextField and use: viewModel.string.wrappedValue in the text view
Stuf
Upvotes: 1