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

SwiftUI ForEach循环内变量与枚举动态绑定实现问题

动态数量SwiftUI输入框的焦点绑定方案

不要用固定枚举做焦点标识,FocusState、AccessibilityFocusState的绑定值只需要遵循Hashable协议,不需要强制实现CaseIterable,完全可以用列表索引作为动态标识,配合数组存储状态适配任意数量的输入字段。

核心修改点

  • 替换固定枚举的焦点类型:直接用Int?作为焦点绑定值类型,输入框的索引本身就是唯一可哈希的标识,不需要提前定义枚举case,用户输入任意数量的字段都能适配。
  • 替换单个@State字符串为字符串数组:数组长度和用户设定的字段数量保持同步,每个索引位置对应一个输入框的内容。
  • 动态同步数组长度:当用户修改字段数量X时,同步增减输入内容数组的长度,保证索引和输入框一一对应。

修正后可运行代码

struct TestView: View {
    @AccessibilityFocusState var accessFocusedIndex: Int?
    @FocusState var focusedIndex: Int?
    // 数组存储所有输入框内容
    @State private var fieldValues: [String] = []
    // 用户输入的字段数量X
    @State private var fieldCount: Int = 5

    var body: some View {
        VStack(spacing: 16) {
            // 示例:字段数量调整控件
            Stepper("当前输入字段数量:\(fieldCount)", value: $fieldCount, in: 1...200)
                .padding(.horizontal)
                .onChange(of: fieldCount) { newCount in
                    // 同步调整内容数组长度
                    if fieldValues.count < newCount {
                        fieldValues.append(contentsOf: Array(repeating: "", count: newCount - fieldValues.count))
                    } else if fieldValues.count > newCount {
                        fieldValues.removeLast(fieldValues.count - newCount)
                        // 如果当前聚焦的字段被删除,重置焦点
                        if let focused = focusedIndex, focused >= newCount {
                            focusedIndex = nil
                            accessFocusedIndex = nil
                        }
                    }
                }

            ScrollView {
                LazyVStack(spacing: 12) {
                    // 按索引遍历动态生成输入框
                    ForEach(0..<fieldCount, id: \.self) { index in
                        TextField("第\(index + 1)组输入字段", text: $fieldValues[index])
                            .padding(10)
                            .textFieldStyle(.roundedBorder)
                            // 绑定焦点状态,匹配当前索引即聚焦
                            .focused($focusedIndex, equals: index)
                            .accessibilityFocused($accessFocusedIndex, equals: index)
                    }
                }
                .padding(.horizontal)
            }

            // 示例:主动控制焦点跳转
            Button("聚焦到最后一个输入框") {
                let lastIndex = fieldCount - 1
                focusedIndex = lastIndex
                accessFocusedIndex = lastIndex
            }
            .buttonStyle(.borderedProminent)
        }
        .onAppear {
            // 视图加载时初始化对应数量的空输入值
            fieldValues = Array(repeating: "", count: fieldCount)
        }
    }
}

注意:如果列表存在删除、重排操作,不要直接用索引作为ForEach的id,可以给每个输入字段生成唯一的UUID标识,把焦点类型替换为UUID?即可,逻辑完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:54:16