'Call function (which generates list to be shown) for each character entered into TextField
I want my GUI to update and react to values that the user types into a TextField as it is typed in. I have a function that calculates the result as an array of strings, which is then displayed.
However, I have not been able to achieve this - currently I have had to resort to a submit button that the user has to press when finished entering the value.
So, is there a way to react on each digit entered? Please see my example code!
First the simple GUI:
struct ContentView: View {
@State var initialValue = ""
@ObservedObject var model = ImmediateUpdaterModel()
var body: some View {
VStack{
TextField("Enter value", text: $initialValue)
Button(
action: {model.PopulateListBasedOnInputValue(inputValue: convertTextToDouble(inputString: initialValue))
},
label: {Text("Submit")}
)
ScrollView {
LazyVStack {
ForEach(model.displayData,id: \.self) { myString in
Text("\(myString)")
}
}
}
}
}
Then the ViewModel containing the function PopulateListBasedOnInputValue:
class ImmediateUpdaterModel: ObservableObject {
@Published var displayData = [String]()
func PopulateListBasedOnInputValue(inputValue: Double) {
displayData.removeAll()
if inputValue<10 {
displayData.append("That is a low value")
} else if inputValue<100 {
displayData.append("That is a medium value")
} else if inputValue<1000 {
displayData.append("That is a high value")
}
displayData.append("However, all values are good!")
init() {
displayData = []
}
Final clarification of what I want to achieve: If the user aims to write 999, first this is displayed:
That is a low value
However, all values are good!
When the next digit is entered, the text changes to
That is a medium value
However, all values are good!
And when the final 9 is entered the text changes to
That is a high value
However, all values are good!
Solution 1:[1]
Adding the ImmediateUpdaterModel is complicating the situation. You could get it to work by triggering PopulateListBasedOnInputValue using onChange or even Combine, but it seems like the whole thing could be simplified by just building it into the View:
struct ContentView: View {
@State private var textValue = ""
var body: some View {
VStack{
TextField("Enter value", text: $textValue)
ScrollView {
if !textValue.isEmpty {
if let doubleValue = Double(textValue) {
switch doubleValue {
case _ where doubleValue < 10:
Text("That is a low value")
case _ where doubleValue < 100:
Text("That is a medium value")
case _ where doubleValue >= 100:
Text("That is a high value")
default:
Text("")
}
Text("However, all values are good!")
} else {
Text("Invalid character")
}
}
}
}
}
}
You could also separate the logic out into a computed property which would get re-run each time the @State changes. This strategy looks a little closer to your original system:
struct ContentView: View {
@State private var textValue = ""
var displayedData : [String] {
var toReturn = [String]()
if !textValue.isEmpty {
if let doubleValue = Double(textValue) {
switch doubleValue {
case _ where doubleValue < 10:
toReturn.append("That is a low value")
case _ where doubleValue < 100:
toReturn.append("That is a medium value")
case _ where doubleValue >= 100:
toReturn.append("That is a high value")
default:
break
}
toReturn.append("However, all values are good!")
} else {
toReturn.append("Invalid character")
}
}
return toReturn
}
var body: some View {
VStack{
TextField("Enter value", text: $textValue)
ScrollView {
ForEach(displayedData, id: \.self) { item in //generally, you want to avoid using \.self as an id, but here we can guarantee all of the strings are unique
Text(item)
}
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jnpdx |
