【发布时间】:2021-05-02 22:53:30
【问题描述】:
我有一个带有 ForEach 的视图,其中包含另一个视图的多个实例。我希望能够:
- 单击主视图中的按钮并触发嵌套视图的验证,然后
- 在返回的路上,用验证结果填充一个数组
我已经简化了项目,以便可以复制它。这是我所拥有的:
import SwiftUI
final class AddEditItemViewModel: ObservableObject {
@Published var item : String
@Published var isValid : Bool
@Published var doValidate: Bool {
didSet{
print(doValidate) // This is never called
validate()
}
}
init(item : String, isValid : Bool, validate: Bool) {
self.item = item
self.isValid = isValid
self.doValidate = validate
}
func validate() { // This is never called
isValid = Int(item) != nil
}
}
struct AddEditItemView: View {
@ObservedObject var viewModel : AddEditItemViewModel
var body: some View {
Text(viewModel.item)
}
}
final class AddEditProjectViewModel: ObservableObject {
let array = ["1", "2", "3", "nope"]
@Published var countersValidationResults = [Bool]()
@Published var performValidation = false
init() {
for _ in array {
countersValidationResults.append(false)
}
}
}
struct ContentView: View {
@ObservedObject var viewModel : AddEditProjectViewModel
@State var result : Bool = false
var body: some View {
VStack {
ForEach(
viewModel.countersValidationResults.indices, id: \.self) { i in
AddEditItemView(viewModel: AddEditItemViewModel(
item: viewModel.array[i],
isValid: viewModel.countersValidationResults[i],
validate: viewModel.performValidation
)
)
}
Button(action: {
viewModel.performValidation = true
result = viewModel.countersValidationResults.filter{ $0 == false }.count == 0
}) {
Text("Validate")
}
Text("All is valid: \(result.description)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(viewModel: AddEditProjectViewModel())
}
}
当我在主视图中更改属性时,嵌套视图中的属性不会更改,即使它是 @Published 属性。
由于第一步不起作用,我什至无法测试第二部分(使用验证结果更新书籍数组)
我需要这样的设置,因为如果项目无效,该视图将显示错误消息,因此嵌入式视图需要知道它们是否有效。
更新:
我的问题是您似乎无法将绑定对象存储在视图模型中,只能存储在视图中,因此我将属性移到了视图中,并且它可以工作:
import SwiftUI
final class AddEditItemViewModel: ObservableObject {
@Published var item : String
init(item : String) {
self.item = item
print("item",item)
}
func validate() -> Bool{
return Int(item) != nil
}
}
struct AddEditItemView: View {
@ObservedObject var viewModel : AddEditItemViewModel
@Binding var doValidate: Bool
@Binding var isValid : Bool
init(viewModel: AddEditItemViewModel, doValidate:Binding<Bool>, isValid : Binding<Bool>) {
self.viewModel = viewModel
self._doValidate = doValidate
self._isValid = isValid
}
var body: some View {
Text("\(viewModel.item): \(isValid.description)").onChange(of: doValidate) { _ in isValid = viewModel.validate() }
}
}
struct ContentView: View {
@State var performValidation = false
@State var countersValidationResults = [false,false,false,false] // had to hard code this here
@State var result : Bool = false
let array = ["1", "2", "3", "nope"]
// init() {
// for _ in array {
// countersValidationResults.append(false) // For some weird reason this appending doesn't happen!
// }
// }
var body: some View {
VStack {
ForEach(array.indices, id: \.self) { i in
AddEditItemView(viewModel: AddEditItemViewModel(item: array[i]), doValidate: $performValidation, isValid: $countersValidationResults[i])
}
Button(action: {
performValidation.toggle()
result = countersValidationResults.filter{ $0 == false }.count == 0
}) {
Text("Validate")
}
Text("All is valid: \(result.description)")
Text(countersValidationResults.description)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
【问题讨论】: