将ForEach与绑定数组(SwiftUI)一起使用

erd*_*dos 5 swift swiftui

我的目标是从JSON动态生成表单。除了将FormField视图(基于TextField)与绑定到动态生成的视图模型列表的绑定之外,我已经将所有内容组合在一起。

如果我将FormField视图换成普通的Text视图,则可以正常工作(请参见屏幕截图):

ForEach(viewModel.viewModels) { vm in
    Text(vm.placeholder)
}
Run Code Online (Sandbox Code Playgroud)

对于

ForEach(viewModel.viewModels) { vm in
     FormField(viewModel: $vm)
}
Run Code Online (Sandbox Code Playgroud)

我试图使@State var 的viewModels属性ConfigurableFormViewModel,但它失去了其可编码性。JSON>绑定<[FormFieldViewModel]自然不会真正起作用。

这是我的代码的要点:

JSON格式的屏幕截图,但使用<code> Text </ code>

nic*_*ste 21

您可以尝试的第一件事是:

ForEach(0 ..< numberOfItems) { index in
   HStack {
     TextField("PlaceHolder", text: Binding(
       get: { return items[index] },
       set: { (newValue) in return self.items[index] = newValue}
     ))
   }
}
Run Code Online (Sandbox Code Playgroud)

前一种方法的问题在于,如果numberOfItems某个方法是动态的,并且可能会因 Button 的动作而发生变化,则它不会起作用,并且会引发以下错误:ForEach<Range<Int>, Int, HStack<TextField<Text>>> count (3) != its initial count (0). 'ForEach(_:content:)' should only be used for *constant* data. Instead conform data to 'Identifiable' or use 'ForEach(_:id:content:)' and provide an explicit 'id'!

如果你有这个用例,你可以做这样的事情,即使在 SwiftView 的生命周期中项目增加或减少,它也会工作:

ForEach(items.indices, id:\.self ){ index in
   HStack {
     TextField("PlaceHolder", text: Binding(
       get: { return items[index] },
       set: { (newValue) in return self.items[index] = newValue}
     ))
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!我一直在寻找这个答案!您还可以执行“self.$items[index]”,而不是创建自定义“Binding”。 (3认同)
  • 请注意:当您以这种方式使用索引值时,ForEach 视图仅适用于静态内容。如果动态更改内容,则可能会遇到错误。 (3认同)

Raj*_*han 12

雨燕5.5

从 Swift 5.5 版本开始,您可以通过像这样传入 bindable 来直接使用绑定数组。

ForEach($viewModel.viewModels, id: \.self) { $vm in
     FormField(viewModel: $vm)
}
Run Code Online (Sandbox Code Playgroud)


erd*_*dos 8

尝试不同的方法。FormField 维护它自己的内部状态并在其文本提交时发布(通过完成):

struct FormField : View {
    @State private var output: String = ""
    let viewModel: FormFieldViewModel
    var didUpdateText: (String) -> ()

    var body: some View {
        VStack {
            TextField($output, placeholder: Text(viewModel.placeholder), onCommit: {
                self.didUpdateText(self.output)
            })

            Line(color: Color.lightGray)
        }.padding()
    }
}
Run Code Online (Sandbox Code Playgroud)
ForEach(viewModel.viewModels) { vm in
    FormField(viewModel: vm) { (output) in
        vm.output = output
    }
}
Run Code Online (Sandbox Code Playgroud)


Lor*_*ngo 5

解决方案可能如下:

ForEach(viewModel.viewModels.indices, id: \.self) { idx in
     FormField(viewModel:  self.$viewModel.viewModels[idx])
}
Run Code Online (Sandbox Code Playgroud)