SwiftUI:@State 变量永远不会从@Published 更新

SwiftUI: @State variable never get updated from @Published

我正在尝试在模型出现错误时触发警报,但它从未更新以显示警报:

这是我在视图中的实现:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel
    @State var showAlert = false
    init() {
        viewModel = ViewModel()
        showAlert = viewModel.showAlert
    }
    var body: some View {
        NavigationView {
            Text("Hello, world!")
                .padding()
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text("This works"),
                  message: Text("Hello"),
                  dismissButton: .default(Text("got it"))
        )}
    }
}

这是我的模型:

class ViewModel: ObservableObject {
    @Published var showAlert = false
    var cancellables = Set<AnyCancellable>()
    
    init() {
        DoSomething.shared.showAlert.sink { _ in
            print("got new Value")
        } receiveValue: {[weak self] value in
            print("value")
            self?.showAlert = value
        }.store(in: &cancellables)
    }
}
class DoSomething {
    let showAlert = PassthroughSubject<Bool, Never>()
    static let shared = DoSomething()
    private init() {
        checkToShowAlert()
    }
    func checkToShowAlert() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
            print("change value")
            self?.showAlert.send(true)
        }
    }
}

你们中有人知道为什么 showAlert 变量永远不会更新吗?

非常感谢你的帮助

在您当前的代码中,您在 那个时间点将 ContentViewshowAlert 设置为 ViewModelshowAlert :

init() {
  viewModel = ViewModel()
  showAlert = viewModel.showAlert //<-- assignment at the time of init
}

意思是,赋值时是false。因为它只是一个 Bool 被分配给另一个 Bool,所以没有机制在 ViewModelshowAlert 发生变化时保持更新。

最简单的解决方案是摆脱您的 @State 变量并直接观察 @Published 属性:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel = ViewModel()

    var body: some View {
        NavigationView {
            Text("Hello, world!")
                .padding()
        }
        .alert(isPresented: $viewModel.showAlert) {
            Alert(title: Text("This works"),
                  message: Text("Hello"),
                  dismissButton: .default(Text("got it"))
        )}
    }
}

摆脱视图模型对象,这是主要问题。我们不在 SwiftUI 中使用它们。我们对视图数据使用 View 结构,并使用 @State@Binding 属性 包装器使结构表现得像一个对象,因此您需要先了解它。

此外,我认为您不需要 Combine 来完成您想要做的事情,因为您没有使用 combineLatest 等组合任何东西,但是当我们在 SwiftUI 中使用它时,我们不使用 sinkstore,而不是我们 assign 管道的末端到 @Published.

示例:

struct ContentView: View {
    @State var isPresented = false

    var body: some View {
        NavigationView {
            VStack{
                Text("Hello, world!")
                  .padding()
                Button("Show Alert") {
                    showAlert()
                 }
            }
        }
        .alert(isPresented: $isPresented) {
            Alert(title:Text("This works"),
                  message: Text("Hello"),
                  dismissButton: .default(Text("got it"))
        )}
    }
    
    func showAlert() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            isPresented = true
        }
    }
}