I'm trying to implement a TextField with a number input together with Stepper to control quantity. After entering a number in the TextField the Stepper loses the ability to change the number. I'm pretty sure there is a trick with Binding value, but can't figure out what exactly.
struct TestView: View {
@State var quantity: Int = 0
var body: some View {
HStack {
TextField("", value: $quantity, formatter: NumberFormatter())
Stepper("", onIncrement: {
self.quantity += 1
}, onDecrement: {
self.quantity -= 1
})
}
}
}
This is because using NumberFormatter
in TextField
is bugged .
You may need to use a custom binding instead:
struct ContentView: View {
@State var quantity: Int = 0
static let formatter = NumberFormatter()
var binding: Binding<String> {
.init(get: {
"\(self.quantity)"
}, set: {
self.quantity = Int($0) ?? self.quantity
})
}
var body: some View {
HStack {
TextField("", text: binding)
Stepper("", onIncrement: {
self.quantity += 1
}, onDecrement: {
self.quantity -= 1
})
}
}
}
Also don't recreate NumberFormatter
every time:
TextField("", value: $quantity, formatter: NumberFormatter())
You can use a static property which is only created once:
static let formatter = NumberFormatter()
I had a similar issue with Stepper
and TextField
so I decided to make a Swift package that solves the issue.
After entering a number in the TextField the Stepper loses the ability to change the number.
I was facing this issue too and actually found that the onIncrement
and onDecrement
actions continue to be accordingly fired. However, I noticed that engaging with the Stepper
doesn't release the user focus on the TextField
as observed through the continued blinking cursor.
Removing the focus from the TextField
while the user engages with the Stepper
resolves the issue.
This also makes sense when consulting the TextField
documentation:
If the value is a string, the text field updates this value continuously as the user types or otherwise edits the text in the field. For non-string types, it updates the value when the user commits their edits, such as by pressing the Return key.
(Source: https://developer.apple.com/documentation/swiftui/textfield )
As such, I'd recommend simulating user commit events when the user engages with the Stepper
. This avoids unnecessarily type-casting and allows you to continue to take advantage of the TextField
formatter
initialiser.
Example modification of the original code:
struct TestView: View {
@State var quantity: Int = 0
// Keep track of which field the user is focused on
@FocusState private var focusedField: String?
var body: some View {
HStack {
TextField("", value: $quantity, formatter: NumberFormatter())
.focused($focusedField, equals: "quantity")
Stepper("", onIncrement: {
// Remove the focus from the (text) field
focusedField = nil
self.quantity += 1
}, onDecrement: {
// Remove the focus from the (text) field
focusedField = nil
self.quantity -= 1
})
}
}
}
For more information, also see the focused(_:equals:)
and @FocusState
documentation: https://developer.apple.com/documentation/swiftui/view/focused(_:equals:) https://developer.apple.com/documentation/SwiftUI/FocusState
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.