使用viewModel绑定UITextField或按钮

时间:2018-02-05 11:59:59

标签: ios swift mvvm rx-swift

我一直面临着在ViewModel中将UITextField或按钮与observables绑定的问题。

class VM {
    var emailObservable: Observable<String?> = Observable.just("")
}

我在viewModel和控制器中有这个可观察的电子邮件。当我尝试用它绑定我的文本字段时,它给了我错误

  

无法使用'(to:Observable)'类型的参数列表调用'bind'。

但是当我用Variable替换observable时,它运行正常。

有人可以帮我解决这个问题。我发现答案主要包括在viewModel的init方法中传递observable,但我不想在init方法中传递它。

这是我发现绑定的链接,但它是通过init方法。

How to bind rx_tap (UIButton) to ViewModel?

5 个答案:

答案 0 :(得分:1)

我想这就是你要找的东西:

final class ViewModel {

    private let bag = DisposeBag()
    let string = BehaviorSubject<String>(value: "")

    init() {
        string.asObservable().subscribe(onNext: { string in
            print(string)
        })
        .disposed(by: bag)
    }

}

final class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    private let bag = DisposeBag()
    private var viewModel: ViewModel!

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel = ViewModel()

        textField.rx.text
            .orEmpty
            .bind(to: viewModel.string)
            .disposed(by: bag)
    }

}

请注意,由于@MaximVolgin提及Variable is deprecated in RxSwift 4,因此您可以使用BehaviorSubject或其他由您决定的内容。

UPD。

仅使用Observable实施。

final class ViewModel {

    private let bag = DisposeBag()

    var string = "" {
        didSet {
            print(string)
        }
    }

    init(stringObservable: Observable<String>) {
        stringObservable.subscribe(onNext: { string in
            self.string = string
        })
        .disposed(by: bag)
    }

}

final class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    private let bag = DisposeBag()
    private var viewModel: ViewModel!

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel = ViewModel(stringObservable: textField.rx.text.orEmpty.asObservable())
    }

}

如您所见,您的解决方案可以使用Observable,而不是Variable或任何类型的Subject来实施。还应该提到的是,在大多数情况下,这不是最终的逻辑(只需将textField绑定到某个变量)。可以有一些验证,启用/禁用等逻辑。对于这种情况,RxSwift提供Driver。关于在一个项目中使用ObservableDriver的差异的好例子可以在here找到(由RxSwift提供)。

答案 1 :(得分:0)

方法 .bind(to:)绑定到 Observer ,而不是 Observable

变量(在RxSwift v4中不推荐使用)是一个特殊用途的主题

主题按照定义 Observer Observable

这就是 .bind(to:)在里面做的事情 -

public func bind<O: ObserverType>(to observer: O) -> Disposable where O.E == E {
    return self.subscribe(observer)
}

<强>更新

如何避免在VM的 .init()中传递observable:

// inside VM:

fileprivate let observableSwitch: BehaviorSubject<Observable<MyValue>>
fileprivate let myValueObservable = observableSwitch.switchLatest()

// instead of passing in init:

public func switch(to observable: Observable<MyValue>) {
  self.observableSwitch.onNext(observable)
}

答案 2 :(得分:0)

在ViewModel类中使用变量类型的主题:

class ViewModel{

//MARK: - local Variables
var emailText = Variable<String?>("")
}       

现在在viewController类中创建viewmodel类的对象,并将此emailtext变量绑定到viewcontroller中的textfield。每当textfield文本发生变化时,viewmodel的emailText都会获得值。

txtfield.rx.text
        .bindTo(viewModel.emailText).addDisposableTo(disposeBag)

答案 3 :(得分:0)

而不是

emailTextfield.rx.text.asObservable().bind(to: viewModel.emailObservable).disposed(by: disposeBag)

使用此代码

viewModel.emailObservable.bind(to: noteField.rx.text).disposed(by: disposeBag)

您可能希望进行双向绑定,因此请详细了解here

答案 4 :(得分:0)

试试这个,

override func viewDidLoad() {
        super.viewDidLoad()
        _ = userNameTextField.rx.text.map { $0 ?? "" }.bind(to: viewModel.userName)
    }

在viewModel类中,

class ViewModel{
   var userName: Variable<String> = Variable("")
}