如何响应外部更改来更新视图状态?

时间:2020-02-28 13:09:01

标签: swift swiftui combine

想象一下我的视图具有某种可变状态,但是可能需要更新该状态以反映另一个对象(例如ViewModel)中的更改。

如何在SwiftUI中实现呢?

我已经尝试了以下方法,但是无法获得反映来自ViewModel的更新的视图:

class ViewModel: ObservableObject {

    @Published var text: String = "loading"

    private var task: AnyCancellable?

    func fetch() {
        task = Just("done")
            .delay(for: 1, scheduler: RunLoop.main)
            .assign(to: \.text, on: self)
    }
}

struct ContentView: View {

    @ObservedObject var viewModel = ViewModel()

    @State var viewText = "idle"

    private var bind: AnyCancellable?

    init() {
        viewText = viewModel.text
        bind = viewModel
            .$text
            .print()
            .assign(to: \.viewText, on: self)
    }

    var body: some View {
        VStack {
            TextField(titleKey: "editable text", text: $viewText)
            Text(viewText)
            Text(viewModel.text)
        }
        .onAppear {
            self.viewModel.fetch()
        }
    }
}

TextField和第一个Text元素从ContentView.viewText获取内容,第二个Text直接到达源ViewModel.text

如预期的那样,第二个Text显示"loading",然后显示"done"。第一个Text"idle"不变。

2 个答案:

答案 0 :(得分:2)

这是可行的方法(已测试并可以在Xcode 11.2 / iOS 13.2上使用)-仅修改ContentView

struct ContentView: View {

    @ObservedObject var viewModel = ViewModelX()
    @State private var viewText = "idle"

    init() {
        _viewText = State<String>(initialValue: viewModel.text)
    }

    var body: some View {
        VStack {
            Text(viewText)
            Text(viewModel.text)
        }
        .onReceive(viewModel.$text) { value in
            self.viewText = value
        }
        .onAppear {
            self.viewModel.fetch()
        }
    }
}

答案 1 :(得分:2)

如果下一个屏幕录像看起来像回答您的问题

enter image description here

它是用下一个代码记录的

import SwiftUI
import Combine

class ViewModel: ObservableObject {

    @Published var text: String = "loading"

    private var task: AnyCancellable?

    func fetch() {
        task = Just("done")
            .delay(for: 3, scheduler: RunLoop.main)
            .assign(to: \.text, on: self)
    }
}

struct ContentView: View {

    @ObservedObject var viewModel = ViewModel()
    @State var viewText = "idle"

    var body: some View {
        VStack {
            Text(viewText)
            Text(viewModel.text)
        }.onReceive(viewModel.$text.filter({ (s) -> Bool in
            s == "done"
        })) { (txt) in
                self.viewText = txt
        }.onAppear {
            self.viewModel.fetch()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}