As a beginner in SwiftUI, I am creating a custom OTP view which is working fine, but I am facing an issue with switching focus to the next field. I expect that when the user types, it should automatically switch focus to the next available OTP field.
Please help!!!
struct CustomOtpFieldView: View {
@Binding var text: String
@Binding var otpFields: [String]
var index: Int
@FocusState private var isFocused: Bool
var body: some View {
TextField("", text: $text)
.focused($isFocused)
.keyboardType(.numberPad)
.textContentType(.oneTimeCode)
.frame(width: 50, height: 50)
.multilineTextAlignment(.center)
.background(RoundedRectangle(cornerRadius: 5).stroke(Color.green))
.onChange(of: text) { newValue in
let allowedCharacters = "0123456789"
if !allowedCharacters.contains(newValue) {
text = String(newValue.filter { allowedCharacters.contains($0) })
}
if text.count > 1 {
text = String(text.prefix(1))
}
handleOtpFieldChange()
}
.onAppear {
if index == 0 {
isFocused = true
}
}
}
private func handleOtpFieldChange() {
if text.count == 1 && index < otpFields.count - 1 {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
isFocused = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
otpFields[index + 1] = ""
isFocused = true
}
}
}
}
}
@State private var otpFields: [String]
self.fieldsCount = 6
self._otpFields = State(initialValue: Array(repeating: "", count: fieldsCount))
ForEach(0..<fieldsCount, id: .self) { index in
CustomOtpFieldView(text: $otpFields[index], otpFields: $otpFields, index: index)
}
Thanks in advance.
2
Answers
Yeah the way you’re doing it seems a little off, here is how I did it. First create an enum FocusField this will contain all our fields.
Then I created a reusable View called ‘SingleDigitTextFieldView’ that does the validation for me on the onChange modifier. This is how it looks like.
Then you can use it like this.
You can do more validation in the onChange modifier. Happy Coding.
I created this reusable OTPFieldView SwiftUI component where I’ve utilized the @FocusState property wrapper and created a FocusPin enum to manage focus on each field dynamically. The onChange handler on each TextField plays a key role in moving the focus to the next field once a digit is entered
I hope it proves helpful for others facing the same challenge.
You can download the full source code in my Github repo
https://github.com/JayantBadlani/OTPFieldView-SwiftUI
SOURCE CODE:
EXAMPLE: HOW TO USE