简体   繁体   English

将UITextField或按钮与viewModel绑定

[英]Binding UITextField or button with viewModel

I have been facing an issue with binding UITextField or button with observables in viewModel. 我一直在将UITextField或按钮与viewModel中的可观察对象绑定时遇到问题。

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

I have this observable for email in my viewModel and in controller. 我在viewModel和控制器中都有可观察到的电子邮件。 When i try to bind my textfield with it, it gives me error 当我尝试用它绑定文本框时,它给我错误

Cannot invoke 'bind' with an argument list of type '(to: Observable)'. 无法使用类型为((to:Observable)'的参数列表调用'bind'。

But when i replace the observables with Variable, it works fine. 但是,当我用变量替换可观察对象时,它可以正常工作。

Can someone please help me with this. 有人可以帮我吗 I found answers which mainly include passing the observable in the init method of viewModel, but i don't want to pass it in the init method. 我找到了答案,主要包括在viewModel的init方法中传递observable,但我不想在init方法中传递它。

This is the link i found for binding but it is through init method. 这是我发现用于绑定的链接,但这是通过init方法进行的。

How to bind rx_tap (UIButton) to ViewModel? 如何将rx_tap(UIButton)绑定到ViewModel?

I think here what you looking for: 我认为您在这里寻找什么:

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)
    }

}

Note, as @MaximVolgin mentioned Variable is deprecated in RxSwift 4 , so you can use BehaviorSubject or other that's up to you. 注意,由于@MaximVolgin提到的Variable在RxSwift 4中已弃用 ,因此您可以使用BehaviorSubject或其他取决于您的行为。


UPD. UPD。

Implementation with Observable only. 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())
    }

}

As you can see, your solution can be implemented using Observable , not Variable or any kind of Subject . 如您所见,可以使用Observable而不是Variable或任何Subject来实现您的解决方案。 Also should be mentioned that in most cases this is not the final logic (just bind textField or whatever to some variable). 还应该提到的是,在大多数情况下,这不是最终的逻辑(只需将textField或其他内容绑定到某个变量)。 There can be some validation, enable/disable, etc. logic. 可能会有一些验证,启用/禁用等逻辑。 For this cases RxSwift provide Driver . 对于这种情况,RxSwift提供了Driver Also nice example about differences in using Observable and Driver for one project can be found here (by RxSwift). 此处 (通过RxSwift)也可以找到关于在一个项目中使用ObservableDriver区别的一个很好的示例。

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

Variable (deprecated in RxSwift v4) is a special-purpose Subject . 变量 (在RxSwift v4中已弃用)是一个特殊用途的Subject

Subject s are by definition both Observer and Observable . 根据定义, 主题ObserverObservable

This is what .bind(to:) does inside - 这是.bind(to :)在内部执行的操作-

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

UPDATE: 更新:

How to avoid passing observables in .init() of VM: 如何避免在VM的.init()中传递可观察对象:

// 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)
}

Take a subject of variable type in ViewModel class: 在ViewModel类中获取一个变量类型的主题:

class ViewModel{

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

Now create object of viewmodel class in viewController class and bind this emailtext variable to textfield in viewcontroller.Whenever textfield text will change then it emailText of viewmodel gets value. 现在在viewController类中创建viewmodel类的对象,并将此emailtext变量绑定到viewcontroller中的textfield。每当文本字段文本发生变化时,viewmodel的emailText就会获得价值。

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

Instead of 代替

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

use this code 使用此代码

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

Probably, you want to make two way binding , so read more about it here 可能您想进行双向绑定 ,因此请在此处详细了解

Try this, 尝试这个,

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

in viewModel class, 在viewModel类中,

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM