SwiftUI验证码控件设计与功能实现的优化方案咨询
SwiftUI验证码输入优化:多文本框方案的光标切换与错误状态处理
我尝试用6个文本框实现SwiftUI验证码输入,但遇到不少问题:初始只能操作第一个文本框、第一响应者切换卡顿,怀疑多文本框是不是最优方案。需要实现的核心功能:
- 光标流畅前后切换
- 输入错误时所有文本框变红
原实现代码:
import SwiftUI struct VerficationCode: View { @State private var numberOfCells: Int = 6 @State private var currentlySelectedCell = 0 var body: some View { HStack { Group { ForEach(0 ..< self.numberOfCells) { index in CharacterInputCell(currentlySelectedCell: self.$currentlySelectedCell, index: index) } }.frame(width:15,height: 56) .padding(.horizontal) .foregroundColor(.white) .cornerRadius(10) .keyboardType(.numberPad) } } } struct CharacterInputCell: View { @State private var textValue: String = "" @Binding var currentlySelectedCell: Int var index: Int var responder: Bool {return index == currentlySelectedCell } var body: some View { CustomTextField(text: $textValue, currentlySelectedCell: $currentlySelectedCell, isFirstResponder: responder) } } struct CustomTextField: UIViewRepresentable { class Coordinator: NSObject, UITextFieldDelegate { @Binding var text: String @Binding var currentlySelectedCell: Int var didBecomeFirstResponder = false init(text: Binding<String>, currentlySelectedCell: Binding<Int>{ _text = text _currentlySelectedCell = currentlySelectedCell } func textFieldDidChangeSelection(_ textField: UITextField) { DispatchQueue.main.async { self.text = textField.text ?? "" } } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let currentText = textField.text ?? "" guard let stringRange = Range(range, in: currentText) else { return false } let updatedText = currentText.replacingCharacters(in: stringRange, with: string) if updatedText.count <= 1 { self.currentlySelectedCell += 1 } return updatedText.count <= 1 } } @Binding var text: String @Binding var currentlySelectedCell: Int var isFirstResponder: Bool = false func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField { let textField = UITextField(frame: .zero) textField.delegate = context.coordinator textField.textAlignment = .center textField.keyboardType = .decimalPad return textField } func makeCoordinator() -> CustomTextField.Coordinator { return Coordinator(text: $text, currentlySelectedCell: $currentlySelectedCell) } func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) { uiView.text = text if isFirstResponder && !context.coordinator.didBecomeFirstResponder { uiView.becomeFirstResponder() context.coordinator.didBecomeFirstResponder = true } } }
问题分析与优化方案
多文本框是验证码输入的成熟方案,你的问题主要出在响应者管理逻辑缺失、状态分散、错误状态未统一控制上,以下是针对性优化:
1. 核心优化代码
import SwiftUI struct VerificationCodeView: View { @State private var numberOfCells: Int = 6 @State private var currentlySelectedCell = 0 @State private var codeValues = Array(repeating: "", count: 6) @State private var isError = false // 全局错误状态标记 var body: some View { HStack(spacing: 8) { ForEach(0 ..< numberOfCells) { index in CharacterInputCell( textValue: $codeValues[index], currentlySelectedCell: $currentlySelectedCell, index: index, isError: $isError ) .frame(width: 40, height: 56) .border(isError ? .red : .gray, cornerRadius: 10) // 错误时显示红色边框 } } .keyboardType(.numberPad) .onAppear { currentlySelectedCell = 0 // 初始聚焦第一个输入框 } // 示例:输入完成后触发验证逻辑 .onChange(of: codeValues) { newValue in let fullCode = newValue.joined() if fullCode.count == 6 { // 替换为实际验证码验证逻辑 isError = fullCode != "123456" } else { isError = false } } } } struct CharacterInputCell: View { @Binding var textValue: String @Binding var currentlySelectedCell: Int @Binding var isError: Bool let index: Int var isResponder: Bool { index == currentlySelectedCell } var body: some View { CustomTextField( text: $textValue, currentlySelectedCell: $currentlySelectedCell, index: index, isFirstResponder: isResponder ) } } struct CustomTextField: UIViewRepresentable { @Binding var text: String @Binding var currentlySelectedCell: Int let index: Int var isFirstResponder: Bool = false class Coordinator: NSObject, UITextFieldDelegate { @Binding var text: String @Binding var currentlySelectedCell: Int let index: Int init(text: Binding<String>, currentlySelectedCell: Binding<Int>, index: Int) { _text = text _currentlySelectedCell = currentlySelectedCell self.index = index } func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let currentText = textField.text ?? "" guard let stringRange = Range(range, in: currentText) else { return false } let updatedText = currentText.replacingCharacters(in: stringRange, with: string) // 输入字符时跳转到下一个输入框 if !string.isEmpty { text = string if index < 5 { currentlySelectedCell = index + 1 } } else { // 删除字符时跳转到上一个输入框 text = "" if index > 0 { currentlySelectedCell = index - 1 } } return updatedText.count <= 1 } // 点击输入框时切换当前选中项 func textFieldDidBeginEditing(_ textField: UITextField) { currentlySelectedCell = index } } func makeCoordinator() -> Coordinator { Coordinator(text: $text, currentlySelectedCell: $currentlySelectedCell, index: index) } func makeUIView(context: Context) -> UITextField { let textField = UITextField(frame: .zero) textField.delegate = context.coordinator textField.textAlignment = .center textField.keyboardType = .numberPad textField.backgroundColor = .white return textField } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text // 实时同步响应者状态 if isFirstResponder { uiView.becomeFirstResponder() } else { uiView.resignFirstResponder() } } } // 预览 struct VerificationCodeView_Previews: PreviewProvider { static var previews: some View { VerificationCodeView() .padding() .background(Color.gray.opacity(0.1)) } }
2. 关键优化点说明
- 统一状态管理:用
codeValues数组存储所有输入框内容,替代每个输入框独立的@State,便于统一验证和状态同步 - 流畅光标切换:
- 新增删除字符时的向前跳转逻辑
- 点击输入框时自动切换当前选中项
- 移除原代码中
didBecomeFirstResponder的错误标记逻辑,实时同步响应者状态
- 错误状态处理:添加全局
isError变量,统一控制所有输入框的边框颜色,验证逻辑触发时一键更新状态 - 布局修复:调整输入框尺寸和间距,修复原代码中
Group的frame设置错误,优化视觉效果
内容的提问来源于stack exchange,提问作者Sham Dhiman
相关产品推荐
相关产品推荐

