在SwiftUI中动态访问TextField

tpgth1q7  于 2022-12-10  发布在  Swift
关注(0)|答案(1)|浏览(195)

我在VStack中动态创建一个TextField。这里的TextField创建如下:

struct Input: View {
    
    var title: String
    @State var value:String
    var disableInputMode:Bool = true

    var body: some View {                
        VStack {
            Text(title)
                .frame(maxWidth: .infinity, alignment: .leading)
                .foregroundColor(Color("SecondaryTextColor"))
            TextField("", text: $value)
                .disabled(disableInputMode)
                .frame(maxWidth: .infinity, alignment: .leading)
            Divider()
        }
        .padding()        
    }
}

并且这些TextField依赖于其他一些数据字段(在运行时确定):

ScrollView {
    VStack {
        ForEach(detailsViewModel.details) { datum in
            Input(title: datum.title, value: datum.detail!)
        }
    }.frame(maxWidth: .infinity)
    .padding()
}.background(Color("Light"))
.cornerRadius(25)
.padding()

现在,当用户输入数据并尝试应用时,我找不到如何从其他视图获取这些输入。

w6lpcovy

w6lpcovy1#

解决方案1(iOS 14 *)

使用onChange修饰符检测TextField值更改并将其更新到您的模型。

struct Input: View {
    
    var title: String
    @State var value:String
    var disableInputMode:Bool = false
    
    var onTextChange: ((String) -> Void)?

    var body: some View {
        VStack {
            Text(title)
                .frame(maxWidth: .infinity, alignment: .leading)
                .foregroundColor(Color("SecondaryTextColor"))
            TextField("", text: $value)
                .onChange(of: value) {
                    // Called each time value changes.
                    if onTextChange != nil {
                        onTextChange!($0)
                    }
                }
                .disabled(disableInputMode)
                .frame(maxWidth: .infinity, alignment: .leading)
            Divider()
        }
        .padding()
    }
}

解决方案2(iOS 13 *)

扩展绑定以处理自定义onChange

extension Binding {
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { newValue in
                self.wrappedValue = newValue
                handler(newValue)
            }
        )
    }
}

然后,您的输入视图变为

struct Input: View {
    
    var title: String
    @State var value:String
    var disableInputMode:Bool = false
    
    var onTextChange: ((String) -> Void)?

    var body: some View {
        VStack {
            Text(title)
                .frame(maxWidth: .infinity, alignment: .leading)
                .foregroundColor(Color("SecondaryTextColor"))
            TextField("", text: $value.onChange(textChanged))
                .disabled(disableInputMode)
                .frame(maxWidth: .infinity, alignment: .leading)
            Divider()
        }
        .padding()
    }
    
    func textChanged(to value: String) {
        // Called each time value changes.
        if onTextChange != nil {
            onTextChange!(value)
        }
    }
}

如何用途:

这两种解决方案都可以使用输入视图,如下所示。

ScrollView {
    VStack {
        ForEach(detailsViewModel.details) { datum in
            Input(title: datum.title, value: datum.detail) { newValue in
                // Each time you will get the update
                // Do what ever you want to do with the new value
                // something like
                // detailsViewModel.update(value: newValue, forItem: datum.id)
                print("Text value for \(datum.id) :", newValue)
            }
        }
    }.frame(maxWidth: .infinity)
    .padding()
}.background(Color("Light"))
.cornerRadius(25)
.padding()

相关问题