A simple SwiftUI TextField that limits user input to numbers.
The most common response in https://stackoverflow.com/questions/58733003/swiftui-how-to-create-textfield-that-only-accepts-numbers (the top search result), advocates just setting a numeric keyboard. But that totally misses cases when used with hardware keyboards. Many of the suggested
This NumericTextModifier observes a String value for changes, and when it is changed, it filters out any non-numeric characters and updates the string. Then it converts that string to a NSNumber for easier use. You can choose to allow integers or floating point.
NumericTextField uses NumbericTextModifier and exposes only the NSNumber in the text. It prevents any non-numeric text input, no matter the source (paste, external keyboard). It also manages the keyboard type to match the type of numbers you said to allow.
Standard TextFields have a Formatter that you can pass in, that will be used to format/validate input. However this only occurs when the user finishes editing, not for every keystroke. So a user can type 123abc4 and see that in the text field, then when they hit return it will change to 1234. That's really not ideal. With NumericTextField when they type a non-numeric character it is ignored and never shows up in the text field.
It works just like TextField but you are binding it to NSNumber? instead of a String.
// Inside your view
@State private var int: NSNumber?
@State private var double: NSNumber?
var body: Some View {
VStack {
NumericTextField("Int", number: $int, isDecimalAllowed: false)
NumericTextField("Double", number: $double, isDecimalAllowed: true)
}
}
// Inside your view
@State private var int: NSNumber?
@State private var intString = ""
@State private var double: NSNumber?
@State private var doubleString = ""
var body: Some View {
VStack {
TextField("Int", text: $intString)
.numericText(text: $intString, number: $int, isDecimalAllowed: false)
TextField("Double", text: $doubleString)
.numericText(text: $doubleString, number: $double, isDecimalAllowed: true)
}
}
Use Swift Package Manager or just drag and drop the two source files into your project. It supports iOS 14, macOS 10.16/11, tvOS 14, watchOS 7.
- Ditch
NSNumber?as the bound value, and allow binding eitherInt?orDouble?. I took a quick shot at this, but my generics skills weren't sufficient to figure it out. - Support the other initializers that
TextFieldsupports.
Pull requests are welcome to help with these or any other things you may find or think of.