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

SwiftUI TextField代码优化咨询:能否用ForEach实现动态添加?

SwiftUI 动态TextField代码重构方案

当前代码实现了用户在TextField输入内容时动态显示新的TextField,但存在大量冗余重复代码,可通过数组替代单个变量+ForEach循环进行彻底重构,大幅精简代码同时保留原有功能。


一、重构Trip模型

将原本的多个独立bol变量替换为字符串数组,既符合数据结构逻辑,又能避免重复定义:

import FirebaseFirestore
import MetaCodable

@Codable
struct Trip: Identifiable, Equatable, Hashable {
    @DocumentID @Default(UUID().uuidString) var id: String?
    // 用数组替代多个独立bol变量
    @Default([]) var bols: [String]
    
    // 兼容原有初始化逻辑,最多保留10个空字符串(对应原bol到bol9)
    init(bols: [String] = Array(repeating: "", count: 10)) {
        // 确保数组长度不超过10,避免数据溢出
        self.bols = Array(bols.prefix(10))
    }
}

注:如果需要兼容原有Firestore中已存储的bol/bol1等字段,可通过自定义编码/解码逻辑映射到数组,或者逐步迁移数据。


二、保留TextFieldRowView(无需修改)

原有的TextFieldRowView组件复用性良好,直接保留即可:

struct TextFieldRowView: View {
    let title: String
    let placeholder: String
    let width: CGFloat
    @Binding var text: String
    
    var body: some View {
        ViewThatFits(in: .horizontal) {
            LabeledContent {
                TextField(placeholder, text: $text)
                    .frame(width: width, alignment: .leading)
                    .textInputAutocapitalization(.words)
                    .autocorrectionDisabled()
            } label: {
                Text(title)
                    .bold()
            }
            
            VStack {
                Text(title)
                    .bold()
                
                TextField(placeholder, text: $text)
                    .frame(width: width, alignment: .leading)
                    .textInputAutocapitalization(.words)
                    .autocorrectionDisabled()
            }
        }
    }
}

三、彻底重构AddEditTripView

用数组管理所有bol输入值,通过ForEach循环生成动态TextField,同时简化焦点管理逻辑:

import SwiftUI

struct AddEditTripView: View {
    // 用数组存储所有bol输入,初始10个空值(对应原bol到bol9)
    @State private var bols: [String] = Array(repeating: "", count: 10)
    // 用数组索引作为焦点标识
    @FocusState private var focusedIndex: Int?
    
    private let textFieldWidth: CGFloat = UIScreen.main.bounds.width - 230
    
    var body: some View {
        Form {
            Section {
                // 用ForEach循环生成TextField,只显示到第一个空元素的前一个(或全部10个)
                ForEach(0..<visibleTextFieldCount, id: \.self) { index in
                    TextFieldRowView(
                        title: "BOL:",
                        placeholder: "bol",
                        width: textFieldWidth,
                        text: $bols[index]
                    )
                    .focused($focusedIndex, equals: index)
                    .submitLabel(.next)
                    // 监听输入变化,自动触发下一个TextField的显示与聚焦
                    .onChange(of: bols[index]) { newValue in
                        if !newValue.isEmpty && index < bols.count - 1 {
                            focusedIndex = index + 1
                        }
                    }
                }
            }
        }
    }
    
    // 计算需要显示的TextField数量:找到第一个空元素的位置,若全部非空则显示10个
    private var visibleTextFieldCount: Int {
        if let firstEmptyIndex = bols.firstIndex(where: { $0.isEmpty }) {
            return firstEmptyIndex + 1 // 显示到第一个空元素(包含)
        } else {
            return bols.count // 全部非空时显示所有10个
        }
    }
    
    // 简化焦点跳转逻辑
    private func focusNextField() {
        guard let currentIndex = focusedIndex else { return }
        focusedIndex = currentIndex < bols.count - 1 ? currentIndex + 1 : nil
    }
}

重构优势

  • 彻底消除重复代码:无需手动复制粘贴10次TextField和焦点判断逻辑
  • 扩展性更强:若后续需要增加更多bol输入框,只需修改数组长度即可
  • 逻辑更清晰:通过数组和索引统一管理输入值与焦点状态
  • 维护成本更低:修改样式或逻辑只需改一处,无需同步修改10次

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:07:04