I didn't find any link or guide to change the "return" key to "done" when keyboard open for TextField in SwiftUI.
Is it possible now without customising UITextField?
I didn't find any link or guide to change the "return" key to "done" when keyboard open for TextField in SwiftUI.
Is it possible now without customising UITextField?
You can change the return key for each textField
with a simple modifier called: submitLabel
that takes the return key type:
Also, as you can see, you can have a callback to handle the return key press action just like the old textFieldShouldReturn
function that is accessible by .onSubmit
modifier.
⚠️ Seems like there is a bug on Xcode 13 beta 1 that prevents this modifier from working in some situations.
This is no longer a good solution. In iOS 15 you can now add a .submitLabel(.done)
modifier. Please see Mojtaba's answer for more details.
The best way I found was to just add the package Introspect to your project.
After doing so add in import Introspect
anywhere in your project files.
Then add one of their View Modifiers to your Textfield
to achieve what you want. I believe this is what you want though ⤵
.introspectTextField { textfield in
textfield.returnKeyType = .done
}
It exposes UIKit to be used in SwiftUI. So that Textfield
object you see above there has access to all UITextfield
functionality! This is a package though so be aware it could break in the future but for now this is a good option.
It's just nice because it saves you from making your own UIKit wrapper for every View
To get the go
button on the keyboard. Try changing the keyboardtype to .webSearch.
// Tested on Xcode 12 beta 2 and iOS 14
.keyboardType(.webSearch)
If somebody is looking for wrapping UITextField in UIViewRepresentable then I have some code to share:
struct CustomTextField: UIViewRepresentable {
let tag: Int
let placeholder: String
let keyboardType: UIKeyboardType
let returnVal: UIReturnKeyType
@Binding var text: String
@Binding var activeFieldTag: Int?
var totalFields: Int = 0
@Binding var isSecureTextEntry: Bool
var textColor: UIColor = .pureWhite
var font: UIFont = .nexaBold13
var placeholderTextColor: UIColor = .pureWhite
var placeholderFont: UIFont = .nexaLight13
var onEditingChanged: (Bool) -> Void = { _ in }
var lastActiveFieldTag: Int? {
// Return, if no active field
// (It also means textFieldShouldReturn not called yet OR called for last field)
guard let activeFieldTag = activeFieldTag else {
return nil
}
// Return previous field
if activeFieldTag > 0 {
return activeFieldTag - 1
}
// Return, if no previous field
return nil
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField(frame: .zero)
textField.keyboardType = self.keyboardType
textField.returnKeyType = self.returnVal
textField.tag = self.tag
textField.textColor = textColor
textField.font = font
textField.attributedPlaceholder = NSAttributedString(
string: self.placeholder,
attributes: [
NSAttributedString.Key.foregroundColor: placeholderTextColor,
NSAttributedString.Key.font: placeholderFont,
]
)
textField.delegate = context.coordinator
textField.autocorrectionType = .no
textField.isSecureTextEntry = isSecureTextEntry
return textField
}
func updateUIView(_ textField: UITextField, context: Context) {
if textField.text != self.text {
textField.text = self.text
}
handleFirstResponder(textField)
if textField.isSecureTextEntry != isSecureTextEntry {
textField.isSecureTextEntry = isSecureTextEntry
}
}
func handleFirstResponder(_ textField: UITextField) {
// return if field is neither active nor last-active
if tag != lastActiveFieldTag && tag != activeFieldTag {
return
}
// return if field is already active
if lastActiveFieldTag == activeFieldTag {
return
}
// It creates problem in UI when we press the next button too fast and continuously on keyboard
// // Remove focus from last active field
// if lastActiveFieldTag == tag {
// uiView.removeFocus()
// return
// }
// Give focus to active field
if activeFieldTag == tag {
textField.focus()
return
}
}
// Its called when pressing Next button on the keyboard
// See textFieldShouldReturn
func updateNextTag() {
// There is no next field so set activeFieldTag to nil
if tag + 1 == totalFields {
activeFieldTag = nil
} else {
// Set next field tag as active
activeFieldTag = tag + 1
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITextFieldDelegate {
var parent: CustomTextField
init(_ textField: CustomTextField) {
self.parent = textField
}
func updatefocus(textfield: UITextField) {
textfield.focus()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Give focus to next field
parent.updateNextTag()
parent.text = textField.text ?? ""
// If there is no next active field then dismiss the keyboard
if parent.activeFieldTag == nil {
DispatchQueue.main.async {
textField.removeFocus()
}
}
return true
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
DispatchQueue.main.async {
// To enable user to click on any textField while another is active
self.parent.activeFieldTag = self.parent.tag
self.parent.onEditingChanged(true)
}
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
self.parent.text = textField.text ?? ""
DispatchQueue.main.async {
self.parent.onEditingChanged(false)
}
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text, let rangeExp = Range(range, in: text) {
self.parent.text = text.replacingCharacters(in: rangeExp, with: string)
}
return true
}
}
}
macOS 12.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+
submitLabel(_:)
Sets the submit label for this view.
Form {
TextField("Username", $viewModel.username)
.submitLabel(.done)
}