发布值更改后不会触发 onReceive

onReceive is not triggered after published value has changed

重点是在成功向数据库添加记录后(当我点击确认按钮时),视图应该关闭。问题是它在添加后没有关闭,.onReceive 没有触发,尽管发布者 PassthroughSubject 发送了一个新值。有一件事:如果在单击确认按钮之前触发了警报,则视图将关闭(例如,当并非所有字段都已填写时,将显示警告)

此外,属性 viewModel.nameviewModel.name(对于 TextField's)在添加记录后变为等于空字符串,尽管我没有在任何地方明确地为它们分配这样的值在代码中(好像在这些默认值所在的位置创建了视图模型的新实例)

查看:

struct AddChallengeView: View {
    @Environment(\.presentationMode) var presentationMode
    @ObservedObject var viewModel = AddChallengeViewModel()
    
    
    var body: some View {
        Form{
            Section(header: Text("Name")){
                TextField("Type challenge name", text: $viewModel.name) //viewModel.name == "" after new record added
            }
            
            Section(header: Text("Description")){
                TextEditor(text: $viewModel.description) //viewModel.description == "" after new record added
            }
            //...
            
            Section{
                Button(action: { viewModel.addChallenge()}){
                    HStack{
                        Spacer()
                        Text("Submit").bold()
                        Spacer()
                    }
                }
            }
            
        }.alert(isPresented: $viewModel.showErrorAlert){
            Alert(title: Text("Please, set all values!"))
        }
        .onReceive(viewModel.viewDismissalModePublisher) { shouldDismiss in
        print("new value received") //not printed 
        if shouldDismiss {
            self.presentationMode.wrappedValue.dismiss()
        }
    }
        
    }
}

ViewModel:

class AddChallengeViewModel: ObservableObject{
    var viewDismissalModePublisher = PassthroughSubject<Bool, Never>()
    
    private var shouldDismissView = false {
        print("sending new value") //printed
        didSet {
            viewDismissalModePublisher.send(shouldDismissView)
        }
    }
    
    @Published var showErrorAlert = false

    @Published var name = ""
    @Published var description = ""

    //...
    
    func addChallenge () {
        //...
        
        if (name != "" && description != "" && grounds.count != 0){
            Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid).collection("challenges").addDocument(data: [
                "name": "\(name)",
                "description": "\(description)",
                "grounds": grounds
            ]) { err in
                if let err = err {
                    print("Error adding document: \(err)")
                } else {
                    print("setting new value") //printed
                    self.shouldDismissView = true
                }
            }
        } else {
            showErrorAlert.toggle()
        }
    }
}

更新: 我找到了解决方案。我们需要在 AddChallengeView 中用 @StateObject var viewModel = AddChallengeViewModel() 替换 @ObservedObject var viewModel = AddChallengeViewModel() 但为什么它有效?

but why does it work?

很可能您在 NavigationView(或另一个容器,它在工作流程中重新创建内容)中使用 AddChallengeView,因此

@ObservedObject var viewModel = AddChallengeViewModel()

在每次重新创建此类视图时创建 AddChallengeViewModel class 的新实例,因此之前的任何更改都将丢失。不过

@StateObject var viewModel = AddChallengeViewModel()

保留模型实例(第一次创建)并将其注入到在视图层次结构的相同位置重新创建的相同类型的新视图。此外,新视图会收到有关同一模型的所有更改的通知。

实际上 @StateObject 属性 包装器为 ObservableObject 提供与 @State 为值类型提供相同的行为。