Как новичок в SwiftUI, я создаю собственное представление OTP, которое работает нормально, но у меня возникла проблема с переключением фокуса на следующее поле. Я ожидаю, что когда пользователь вводит текст, он должен автоматически переключить фокус на следующее доступное поле OTP.
Пожалуйста помоги!!!
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)
}
Заранее спасибо.





Да, то, как вы это делаете, кажется немного странным, вот как я это сделал. Сначала создайте перечисление FocusField, оно будет содержать все наши поля.
enum FocusField {
case one
case two
case three
}
Затем я создал многоразовое представление под названием «SingleDigitTextFieldView», которое выполняет за меня проверку модификатора onChange. Вот как это выглядит.
struct SingleDigitTextFieldView: View {
@Binding var text: String
@FocusState.Binding var ff: FocusField?
var assignedField: FocusField
var nextField: FocusField?
var body: some View {
TextField("0", text: $text)
.focused($ff, equals: assignedField)
.keyboardType(.numberPad)
.textContentType(.oneTimeCode)
.frame(width: 50, height: 50)
.multilineTextAlignment(.center)
.background(RoundedRectangle(cornerRadius: 5).stroke(Color.green))
.onChange(of: text) { oldValue, newValue in
let exceptedValues = "0123456789"
if exceptedValues.contains(newValue) {
if newValue.count > 0 {
ff = nextField
}
}
else {
text = ""
}
}
}
}
Тогда вы можете использовать его вот так.
struct ContentView: View {
@FocusState private var ff: FocusField?
@State private var oneText: String = ""
@State private var twoText: String = ""
@State private var threeText: String = ""
var body: some View {
HStack {
SingleDigitTextFieldView(
text: $oneText,
ff: $ff,
assignedField: .one,
nextField: .two
)
SingleDigitTextFieldView(
text: $twoText,
ff: $ff,
assignedField: .two,
nextField: .three
)
SingleDigitTextFieldView(
text: $threeText,
ff: $ff,
assignedField: .three,
nextField: nil
)
}
}
}
Вы можете выполнить дополнительную проверку в модификаторе onChange. Приятного кодирования.
Большое спасибо. это мне очень помогает, но сейчас я рассматриваю возможность создания одного компонента, в котором будет отправляться параметр fieldCount (количество полей OTP), который создает поля OTP соответственно параметру fieldCount.
Посмотрите Demysitfy SwiftUI: использование диапазонов небезопасно. Кроме того, любой учебник, которому вы следуете, действительно старый. Посмотрите официальные, SwiftUI сильно изменился. TextField изначально поддерживает Double и Int.