繁体   English   中英

UITextField使用RxSwift绑定到ViewModel

[英]UITextField binding to ViewModel with RxSwift

我愿意使用RxSwift在模型值和视图控制器之间进行MVVM绑定。 我想按照这个realm.io教程 ,但从那时起绑定显然已经改变,示例代码不能编译。 这是示例代码,我认为我已经修复了最糟糕的拼写错误/丢失的东西:

LoginViewModel.swift

import RxSwift

struct LoginViewModel {

    var username = Variable<String>("")
    var password = Variable<String>("")

    var isValid : Observable<Bool>{
        return Observable.combineLatest(self.username.asObservable(), self.password.asObservable())
        { (username, password) in
            return username.characters.count > 0
                && password.characters.count > 0
        }
    }
} 

LoginViewController.swift

import RxSwift
import RxCocoa
import UIKit

class LoginViewController: UIViewController {
    var usernameTextField: UITextField!
    var passwordTextField: UITextField!
    var confirmButton: UIButton!

    var viewModel = LoginViewModel()

    var disposeBag = DisposeBag()

    override func viewDidLoad() {
        usernameTextField.rx.text.bindTo(viewModel.username).addTo(disposeBag)
        passwordTextField.rx.text.bindTo(viewModel.password).addTo(disposeBag)

        //from the viewModel
        viewModel.rx.isValid.map { $0 }
            .bindTo(confirmButton.rx.isEnabled)
    }
}

控制器绑定不编译。 跟踪正确的方法是非常接近的,因为RxSwift文档非常无用,并且Xcode自动完成没有提示任何有用的东西。

第一个问题是这个绑定,它不编译: usernameTextField.rx.text.bindTo(viewModel.username).addTo(disposeBag)

错误:

LoginViewController.swift:15:35: Cannot invoke 'bindTo' with an argument list of type '(Variable<String>)'

我没试过就试过以下内容:

1) usernameTextField.rx.text.bind(to: viewModel.username).addTo(disposeBag) - 错误仍然存​​在: LoginViewController.swift:15:35: Cannot invoke 'bind' with an argument list of type '(to: Variable<String>)'

2)let _ = viewModel.username.asObservable()。bind(to:passwordTextField.rx.text)

let _ = viewModel.username.asObservable()
            .map { $0 }
            .bind(to: usernameTextField.rx.text)

第二个实际编译,但不起作用(即viewModel.username不会更改)

主要的问题是,我在将参数传递给bindbind(to:时被盲目bind(to:方法,因为自动完成在这里并不真正有用..我使用的是swift 3和Xcode 8.3.2。

@XFreire是对的, orEmpty是缺失的魔法,但是你可以看到你的代码看起来像更新到最新的语法并纠正错误可能是orEmpty

首先是视图模型......

  • 应始终使用let定义Variable类型。 您不想替换变量,只想将新数据合并为一个。
  • 定义isValid的方式,每次绑定/订阅它时都会创建一个新的。 在这个简单的情况下无关紧要,因为你只绑定了一次,但总的来说,这不是一个好的做法。 更好的方法是在构造函数中只使用一次isValid。

完全使用Rx时,通常会发现视图模型由一堆let和一个构造函数组成。 拥有任何其他方法甚至计算属性都是不寻常的。

struct LoginViewModel {

    let username = Variable<String>("")
    let password = Variable<String>("")

    let isValid: Observable<Bool>

    init() {
        isValid = Observable.combineLatest(self.username.asObservable(), self.password.asObservable())
        { (username, password) in
            return username.characters.count > 0
                && password.characters.count > 0
        }
    }
}

和视图控制器。 同样,在定义Rx元素时使用let

  • addDisposableTo()已被弃用,优先于使用addDisposableTo() disposed(by:)
  • bindTo()已被弃用,优先于使用bind(to:)
  • 您不需要viewModel.isValid链中的map
  • 你错过了那条链中的disposed(by:)

在这种情况下,如果在加载后者之前由视图控制器之外的某些内容分配了viewModel ,您实际上可能希望它是一个var。

class LoginViewController: UIViewController {
    var usernameTextField: UITextField!
    var passwordTextField: UITextField!
    var confirmButton: UIButton!

    let viewModel = LoginViewModel()
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        usernameTextField.rx.text
            .orEmpty
            .bind(to: viewModel.username)
            .disposed(by: disposeBag)

        passwordTextField.rx.text
            .orEmpty
            .bind(to: viewModel.password)
            .disposed(by: disposeBag)

        //from the viewModel
        viewModel.isValid
            .bind(to: confirmButton.rx.isEnabled)
            .disposed(by: disposeBag)
    }
}

最后,您的视图模型可以替换为单个函数而不是结构:

func confirmButtonValid(username: Observable<String>, password: Observable<String>) -> Observable<Bool> {
    return Observable.combineLatest(username, password)
    { (username, password) in
        return username.characters.count > 0
            && password.characters.count > 0
    }
}

然后你的viewDidLoad看起来像这样:

override func viewDidLoad() {
    super.viewDidLoad()

    let username = usernameTextField.rx.text.orEmpty.asObservable()
    let password = passwordTextField.rx.text.orEmpty.asObservable()

    confirmButtonValid(username: username, password: password)
        .bind(to: confirmButton.rx.isEnabled)
        .disposed(by: disposeBag)
}

使用此样式,一般规则是依次考虑每个输出。 查找影响该特定输出的所有输入,并编写一个函数,将所有输入作为Observables并将输出生成为Observable。

你应该添加.orEmpty

尝试这个:

usernameTextField.rx.text
    .orEmpty
    .bindTo(self.viewModel. username)
    .addDisposableTo(disposeBag)

...和其余的UITextField相同

text属性是String?类型的控件属性String? 添加orEmpty你转换你的String? 将属性控制为String类型的控件属性。

暂无
暂无

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

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