如何使用RxSwift使ViewController观察对ViewModel变量(Variable <..>)的任何更改?

时间:2018-11-26 10:25:36

标签: ios swift observer-pattern rx-swift

如何查看Variable<...>RxSwift类内Variable值(ViewModel ViewController的变化?

因此,如果我在Variable<..>中拥有的ViewModel的任何值在ViewModel中发生的事情内发生变化,那么ViewController将会被注意到“嘿!ViewModel中的一个或多个Variable <..>已更改!向ViewModel询问更新UI和更新UI所需的数据!“

然后ViewController在ViewController内调用方法updateUI(),并在其中向ViewModel询问状态/状态之类的所有信息以更新UI,例如:

func updateUI() {
  progressBar.hide = viewModel.getProgressBarVisibility()
  errorMessageLabel.hide = viewModel.getErrorMessageVisibility()
  errorMessageLabel.text = viewModel.getErrorMessageText()
  .....
  ...
}

3 个答案:

答案 0 :(得分:0)

如果单个ViewModel的属性值更改,为什么要更新完整的UI?

RxSwift使您能够独立聆听更改,并且可以相应地响应/更改UI。

在我看来,这是您的ViewModel和ViewController类的外观:

class ViewModel {
    private var progressBarVisibiity:Variable<Double> = Variable.init(0.0)
    private var errorMessageVisibiity:Variable<Double> = Variable.init(0.0)
    private var errorMessageLabel:Variable<String> = Variable.init("Default text")

    public func setProgressBarVisibiity(_ value:Double) {
        progressBarVisibiity.value = value
    }

    public func setErrorMessageVisibiity(_ value:Double) {
        errorMessageVisibiity.value = value
    }

    public func setErrorMessageLabel(_ value:String) {
        errorMessageLabel.value = value
    }

    public func observeProgressBarVisibiity() -> Observable<Double> {
        return progressBarVisibiity.asObservable().observeOn(MainScheduler())
    }

    public func observeErrorMessageVisibiity() -> Observable<Double> {
        return errorMessageVisibiity.asObservable().observeOn(MainScheduler())
    }

    public func observeErrorMessageLabel() -> Observable<String> {
        return errorMessageLabel.asObservable().observeOn(MainScheduler())
    }
}

class ViewController {
    let viewModel = ViewModel()
    let disposeBag = DisposeBag()
    func observeViewModelChanges() {
        viewModel
            .observeProgressBarVisibiity()
            .subscribe(onNext: { value in
                self.progressBar.hide = viewModel.getProgressBarVisibility()
            })
            .disposed(by: disposeBag)

        viewModel
            .observeErrorMessageVisibiity()
            .subscribe(onNext: { value in
                self.errorMessageLabel.hide = value
            })
            .disposed(by: disposeBag)

        viewModel
            .observeErrorMessageLabel()
            .subscribe(onNext: { value in
                self.errorMessageLabel.text = value
            })
            .disposed(by: disposeBag)
    }
}

答案 1 :(得分:0)

要更新您的UI,我建议使用一个viewState变量,您可以根据需要在视图模型类中对其进行更新,例如:

/// Making it generic allow you to add your view specific state
public enum ViewState<T> {
    // add all the case you need
    case loading
    case ready(T)
    case failure(Error)
}

然后在您的viewModel类中:

let viewState: Variable<ViewState<YourViewControllerState>> = Variable<ViewState<YourViewControllerState>>(.loading)

其中YourViewControllerState是具有特定情况的枚举:

enum YourViewControllerState {
    case progressBarShowed, //...
}

最后在您的ViewController中:

viewModel.viewState
    .asObservable()
    .observeOn(MainScheduler.instance)
    .subscribe { [weak self] _ in
        self?.updateUI()
    }.disposed(by: disposeBag)

private func updateUI() {
    guard isViewLoaded else {
        return
    }

    switch viewModel.viewState.value {
    case .ready(.progressBarShowed):
        progressBar.hide = viewModel.getProgressBarVisibility()

    case .failure:
        errorMessageLabel.hide = viewModel.getErrorMessageVisibility()
        errorMessageLabel.text = viewModel.getErrorMessageText()
    }
}

答案 2 :(得分:0)

我们可以构造一个双向绑定运算符,您可以只使用bindTo。这是ControlProperty <->变量和变量<->变量的实现:

infix operator <->

@discardableResult func <-><T>(property: ControlProperty<T>, variable: BehaviorSubject<T>) -> Disposable {
    let variableToProperty = variable.asObservable()
        .bind(to: property)

    let propertyToVariable = property
        .subscribe(
            onNext: { variable.onNext($0) },
            onCompleted: { variableToProperty.dispose() }
    )

    return Disposables.create(variableToProperty, propertyToVariable)
}

您可以在以下文章中找到问题的详细答案。     Two way binding in RxSwift