SwiftUI doesn't have this built in yet, but Swift does. To do it in SwiftUI, create a custom text field that conforms to UIViewRepresentable to use the becomeFirstResponder functionality in a text field. Here's how I did it in my project. This example has several optional variables you may not need. Feel free to tweak it to work for you.
struct CustomTextField: UIViewRepresentable
{
@Binding var text: String
@Binding var selectedField: Int
@Binding var secure: Bool // Optional
var placeholder: String = ""
var tag: Int
var keyboardType: UIKeyboardType = .asciiCapable // Optional
var returnKey: UIReturnKeyType = .next // Optional
var correctionType: UITextAutocorrectionType = .default // Optional
var capitalizationType: UITextAutocapitalizationType = .sentences // Optional
func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField
{
let textField = UITextField(frame: .zero)
textField.delegate = context.coordinator
textField.keyboardType = keyboardType
textField.returnKeyType = returnKey
textField.autocorrectionType = correctionType
textField.autocapitalizationType = capitalizationType
textField.tag = tag
textField.isSecureTextEntry = secure
textField.placeholder = placeholder
return textField
}
func makeCoordinator() -> CustomTextField.Coordinator
{
return Coordinator(text: $text, secure: $secure)
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>)
{
uiView.text = text
uiView.isSecureTextEntry = secure
context.coordinator.newSelection = { newSelection in
DispatchQueue.main.async {
self.selectedField = newSelection
}
}
if uiView.tag == self.selectedField
{
uiView.becomeFirstResponder()
}
}
class Coordinator: NSObject, UITextFieldDelegate
{
@Binding var text: String
@Binding var secure: Bool
var newSelection: (Int) -> () = { _ in }
init(text: Binding<String>, secure: Binding<Bool>)
{
_text = text
_secure = secure
}
func textFieldDidChangeSelection(_ textField: UITextField)
{
DispatchQueue.main.async {
self.text = textField.text ?? ""
}
}
func textFieldDidBeginEditing(_ textField: UITextField)
{
self.newSelection(textField.tag)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
if textField.returnKeyType == .done
{
textField.resignFirstResponder()
}
else
{
self.newSelection(textField.tag + 1)
}
return true
}
}
}
Then, in the view with the text fields, assign each one a different tag, indicating the desired order.
struct ContentView: View {
@State private var oneOne = ""
@State private var oneTwo = ""
@State private var twoOne = ""
@State private var twoTwo = ""
@State private var threeOne = ""
@State private var threeTwo = ""
@State private var selectedField: Int = 0
@State private var fieldsSecure: Bool = false
var body: some View {
HStack {
VStack {
CustomTextField(
text: $oneOne,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "1, 1",
tag: 1,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
CustomTextField(
text: $oneTwo,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "1, 2",
tag: 4,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
}
VStack {
CustomTextField(
text: $twoOne,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "2, 1",
tag: 2,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
CustomTextField(
text: $twoTwo,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "2, 2",
tag: 5,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
}
VStack {
CustomTextField(
text: $threeOne,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "3, 1",
tag: 3,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
CustomTextField(
text: $threeTwo,
selectedField: $selectedField,
secure: $fieldsSecure,
placeholder: "3, 2",
tag: 6,
keyboardType: .asciiCapable,
returnKey: .done,
correctionType: .yes,
capitalizationType: .words)
}
}
}