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
相关产品推荐
相关产品推荐

