You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI自定义OTP视图:输入框焦点自动切换异常求助

SwiftUI OTP 输入框焦点自动切换解决方案

问题根源

你的原代码中每个CustomOtpFieldView独立管理自己的@FocusState,导致无法跨视图控制焦点切换,依赖延迟的DispatchQueue也容易出现时序问题。下面是修复后的完整实现:


修复后的父视图

struct ContentView: View {
    private let fieldsCount = 6
    @State private var otpFields: [String]
    @FocusState private var focusedField: Int? // 统一管理全局焦点索引

    init() {
        _otpFields = State(initialValue: Array(repeating: "", count: fieldsCount))
    }

    var body: some View {
        HStack(spacing: 10) {
            ForEach(0..<fieldsCount, id: \.self) { index in
                CustomOtpFieldView(
                    text: $otpFields[index],
                    focusedField: $focusedField,
                    index: index,
                    totalFields: fieldsCount
                )
            }
        }
        .onAppear {
            focusedField = 0 // 初始聚焦第一个输入框
        }
    }
}

修复后的子视图

struct CustomOtpFieldView: View {
    @Binding var text: String
    @Binding var focusedField: Int?
    var index: Int
    var totalFields: Int

    var body: some View {
        TextField("", text: $text)
            .focused($focusedField, equals: index) // 绑定到父视图的全局焦点索引
            .keyboardType(.numberPad)
            .textContentType(.oneTimeCode)
            .frame(width: 50, height: 50)
            .multilineTextAlignment(.center)
            .background(RoundedRectangle(cornerRadius: 5).stroke(Color.green))
            .onChange(of: text) { newValue in
                // 过滤非数字字符
                let filtered = newValue.filter { $0.isNumber }
                if filtered != newValue {
                    text = filtered
                }
                // 限制输入长度为1
                if text.count > 1 {
                    text = String(text.prefix(1))
                }

                handleFieldUpdate()
            }
    }

    private func handleFieldUpdate() {
        if text.count == 1 && index < totalFields - 1 {
            // 输入完成后自动跳转到下一个输入框
            focusedField = index + 1
        } else if text.isEmpty && index > 0 {
            // 删除内容后自动回退到上一个输入框
            focusedField = index - 1
        }
    }
}

关键修改说明

  1. 全局焦点管理:将焦点状态提升到父视图,用focusedField变量跟踪当前聚焦的输入框索引,子视图通过.focused($focusedField, equals: index)绑定到对应位置,彻底解决跨视图焦点控制问题。
  2. 移除冗余延迟:原代码中的DispatchQueue延迟是因为独立焦点状态的时序冲突,统一管理后直接修改focusedField即可即时切换焦点。
  3. 增加回退逻辑:补充了删除字符时的焦点回退处理,符合用户常规交互习惯。
  4. 简化输入过滤:用$0.isNumber替代硬编码字符集,代码更简洁且扩展性更好。

内容的提问来源于stack exchange,提问作者Nicolas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 10:19:55