如何在SwiftUI的TextField中使用预定义值且不覆盖原始值
解决SwiftUI中修改表单参数不影响原始预定义参数的问题
问题背景
我有两个视图ParentView、ChildView,以及Parameter类。需求是让用户选择预定义参数,将其值作为表单的初始值。点击ParentView里的"Add parameter"按钮弹出参数列表,选择后值填入表单的TextField。但现在的问题是,修改ParentView里的TextField内容时,会同时改变原始预定义参数的值,请问怎么避免这种情况?是不是要复制参数用副本填充,还是有更好的办法?
原示例代码
ParentView
struct ParentView: View { @State private var showParameters: Bool = false @State var chosenParameter: Parameter = Parameter(id: UUID(), name: "", notes: "") var body: some View { VStack { Form { Button { showParameters.toggle() } label: { Text("Add parameter") } Section(header: Text("Parameters")) { TextField("parameter", text: $chosenParameter.name) TextField("notes", text: $chosenParameter.notes) } } .sheet(isPresented: $showParameters) { ChildView(chosenParameter: $chosenParameter) } } } } #Preview { ParentView() }
ChildView & Parameter类
struct ChildView: View { @Binding var chosenParameter: Parameter @Environment(\.dismiss) var dismiss var body: some View { NavigationStack { List { ForEach(Parameter.parameterList) {parameter in Button { chosenParameter = parameter dismiss() }label: { Text(parameter.name) } } } } } } #Preview { ChildView(chosenParameter: .constant(Parameter.parameterList[2])) } class Parameter: Identifiable { var id: UUID var name: String var notes: String init(id: UUID, name: String, notes: String) { self.id = id self.name = name self.notes = notes } static var parameterList: [Parameter] = [ Parameter(id: UUID(), name: "Parameter 1", notes: "Notes 1"), Parameter(id: UUID(), name: "Parameter 2", notes: "Notes 2"), Parameter(id: UUID(), name: "Parameter 3", notes: "Notes 3"), ] }
问题原因
问题出在Parameter是类(引用类型),当你把parameterList里的对象赋值给chosenParameter时,传递的是对象的引用,而非新对象。所以修改chosenParameter的属性时,本质是在修改原始预定义参数的内存数据,导致原始值被改动。
解决方法
方案一:将Parameter改为结构体(推荐)
SwiftUI对值类型的支持更好,结构体赋值时会自动创建完整副本,天然隔离原始数据和表单数据。只需要把class改成struct即可,其他代码无需修改:
struct Parameter: Identifiable { var id: UUID var name: String var notes: String init(id: UUID, name: String, notes: String) { self.id = id self.name = name self.notes = notes } static var parameterList: [Parameter] = [ Parameter(id: UUID(), name: "Parameter 1", notes: "Notes 1"), Parameter(id: UUID(), name: "Parameter 2", notes: "Notes 2"), Parameter(id: UUID(), name: "Parameter 3", notes: "Notes 3"), ] }
这样修改表单时,只会改动chosenParameter这个副本,原始的parameterList里的参数完全不受影响。
方案二:保留类类型,实现拷贝初始化器
如果因业务需求必须使用类,给Parameter添加一个拷贝初始化方法,在选择参数时创建原始对象的副本,确保表单修改的是独立的对象:
- 修改
Parameter类,添加拷贝初始化器:
class Parameter: Identifiable { var id: UUID var name: String var notes: String init(id: UUID, name: String, notes: String) { self.id = id self.name = name self.notes = notes } // 拷贝初始化器 init(from parameter: Parameter) { self.id = parameter.id self.name = parameter.name self.notes = parameter.notes } static var parameterList: [Parameter] = [ Parameter(id: UUID(), name: "Parameter 1", notes: "Notes 1"), Parameter(id: UUID(), name: "Parameter 2", notes: "Notes 2"), Parameter(id: UUID(), name: "Parameter 3", notes: "Notes 3"), ] }
- 修改
ChildView里的赋值逻辑,创建副本:
Button { chosenParameter = Parameter(from: parameter) dismiss() } label: { Text(parameter.name) }
这样选择参数时,chosenParameter会指向一个新的Parameter对象,修改表单内容就不会影响原始的预定义参数了。
内容的提问来源于stack exchange,提问作者smg
相关产品推荐
相关产品推荐

