Is it possible to add a .submitLable() to a TextField that has the keyboard type .numberPad. All the information I can find is that it's not possible but that information is 2 years old. So I hope that it might have changed.
I have the following code but the submit label doesn't appear on the keyboard when I actually run the app.
TextField("placeholder", text: $placeholder)
.submitLabel(.next)
.keyboardType(.numberPad)
Is there a way to add a submit label or something of similar functionality to a TextField with a keyboard type of numberPad in SwiftUI or is it just not possible with SwiftUI currently?
The issue is that .submitLabel()
modifies the "return" key display/behavior in the keyboard, but with .numberPad
there is no such key to modify. (You can see this behavior by experimenting with different values for .submitLabel()
using the default keyboard type)
It is possible to add an inputAccessoryView
to a UITextField
with a system defined and pre-localized .done
button. (Or .next
, or several others) It is somewhat cumbersome, though.
For example, using a generic type Value
that conforms to BinaryInteger
:
struct NumberTextField<Value: BinaryInteger>: UIViewRepresentable {
@Binding var value: Value
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.keyboardType = .numberPad
textField.delegate = context.coordinator
textField.inputAccessoryView = createToolbar()
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = "\(value)"
}
func makeCoordinator() -> Coordinator {
Coordinator(value: $value)
}
private func createToolbar() -> UIToolbar {
// if you want behavior other than "dismiss", put it in action:
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(UIApplication.dismissKeyboard))
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let toolbar = UIToolbar()
toolbar.sizeToFit()
toolbar.items = [spacer, doneButton]
// you may want to check the locale for right-to-left orientation,
// if it doesn't automatically re-orient the sequence of items.
return toolbar
}
// I don't recall where I got this code, its purpose is mostly to
// filter out non-numeric values from the input, it may be suboptimal
class Coordinator: NSObject, UITextFieldDelegate {
var value: Binding<Value>
init(value: Binding<Value>) {
self.value = value
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
func textFieldDidEndEditing(_ textField: UITextField) {
// if you use a different protocol than BinaryInteger
// you will most likely have to change this behavior
guard let text = textField.text else { return }
guard let integer = Int(text) else { return }
value.wrappedValue = Value(integer)
}
}
}
Here, UIApplication.dismissKeyboard
is an extension on UIApplication
like this, which essentially tells whatever currently has focus (to show the keyboard) to give it up:
extension UIApplication {
@objc
func dismissKeyboard() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
I have used this with Swift 5.5 and a target of iOS 16.x. It should work with some prior versions of Swift / iOS, but I have not tested it with anything else.