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

我有2个UITextField属性和1个UIButton的授权控制器。 我想将View绑定到ViewModel,但不知道如何操作。 这是我的AuthorizatioVC.swift:

class AuthorizationViewController: UIViewController { let disposeBag = DisposeBag() @IBOutlet weak var passwordTxtField: UITextField! @IBOutlet weak var loginTxtField: UITextField! @IBOutlet weak var button: UIButton! override func viewDidLoad() { super.viewDidLoad() addBindsToViewModel() } func addBindsToViewModel(){ let authModel = AuthorizationViewModel(authClient: AuthClient()) authModel.login.asObservable().bindTo(passwordTxtField.rx_text).addDisposableTo(self.disposeBag) authModel.password.asObservable().bindTo(loginTxtField.rx_text).addDisposableTo(self.disposeBag) //HOW TO BIND button.rx_tap here? } } 

这是我的AuthorizationViewModel.swift:

 final class AuthorizationViewModel{ private let disposeBag = DisposeBag() //input //HOW TO DEFINE THE PROPERTY WHICH WILL BE BINDED TO RX_TAP FROM THE BUTTON IN VIEW??? let authEvent = ??? let login = Variable("") let password = Variable("") //output private let authModel: Observable init(authClient: AuthClient){ let authModel = authEvent.asObservable() .flatMap({ (v) -> Observable in return authClient.authObservable(String(self.login.value), mergedHash: String(self.password.value)) .map({ (authResponse) -> Auth in return self.convertAuthResponseToAuthModel(authResponse) }) }) } func convertAuthResponseToAuthModel(authResponse: AuthResponse) -> Auth{ var authModel = Auth() authModel.token = authResponse.token return authModel } } 

您可以将UIButton上的水龙头转换为Observable,并将其与UITextFields中的两个Observable一起交给ViewModel。

这是您的方案的一个小工作示例。 (我使用了一个小的auth客户端模拟类来模拟来自服务的响应):

ViewController:

 import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let loginTxtField = UITextField(frame: CGRect(x: 20, y: 50, width: 200, height: 40)) let passwordTxtField = UITextField(frame: CGRect(x: 20, y: 110, width: 200, height: 40)) let loginButton = UIButton(type: .RoundedRect) let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1) loginTxtField.backgroundColor = UIColor.whiteColor() view.addSubview(loginTxtField) passwordTxtField.backgroundColor = UIColor.whiteColor() view.addSubview(passwordTxtField) loginButton.setTitle("Login", forState: .Normal) loginButton.backgroundColor = UIColor.whiteColor() loginButton.frame = CGRect(x: 20, y: 200, width: 200, height: 40) view.addSubview(loginButton) // 1 let viewModel = ViewModel( withLogin: loginTxtField.rx_text.asObservable(), password: passwordTxtField.rx_text.asObservable(), didPressButton: loginButton.rx_tap.asObservable() ) // 2 viewModel.authResponse .subscribeNext { response in print(response) } .addDisposableTo(disposeBag) } } 

这是两个有趣的部分:

// 1:我们在初始化时将三个Observable注入ViewModel。

// 2:然后我们订阅ViewModel的输出以在登录完成后接收Auth模型。

ViewModel:

 import RxSwift struct Auth { let token: String } struct AuthResponse { let token: String } class ViewModel { // Output let authResponse: Observable init(withLogin login: Observable, password: Observable, didPressButton: Observable) { let mockAuthService = MockAuthService() // 1 let userInputs = Observable.combineLatest(login, password) { (login, password) -> (String, String) in return (login, password) } // 2 authResponse = didPressButton .withLatestFrom(userInputs) .flatMap { (login, password) in return mockAuthService.getAuthToken(withLogin: login, mergedHash: password) } .map { authResponse in return Auth(token: authResponse.token) } } } class MockAuthService { func getAuthToken(withLogin login: String, mergedHash: String) -> Observable { let dummyAuthResponse = AuthResponse(token: "dummyToken->login:\(login), password:\(mergedHash)") return Observable.just(dummyAuthResponse) } } 

ViewModel在其init方法中获取3个Observable并将它们连接到它的输出:

// 1:将登录文本字段的最新值和密码文本字段的最新值组合到一个Observable中。

// 2:当用户按下按钮时,使用登录文本字段的最新值和密码文本字段的最新值,并使用flatMap将其传递给auth服务。 当auth客户端返回AuthResponse ,将其映射到Auth模型。 将此“链”的结果设置为ViewModelauthResponse输出

第一种方法使用PublishSubject

 class ViewController: UIViewController { @IBOutlet weak var loginBtn: UIButton! var vm: ViewModel? let disposebag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() bindUi() } func bindUi() { (loginBtn.rx.tap).bind(to: vm!.loginSbj).addDisposableTo(disposebag) } } class ViewModel { let loginSbj = PublishSubject() init() { loginSbj.do(onNext: { _ in // do something }) } } 

第二种方法使用Action

 class ViewController: UIViewController { @IBOutlet weak var loginBtn: UIButton! var vm: ViewModel? override func viewDidLoad() { super.viewDidLoad() bindUi() } func bindUi() { loginBtn.rx.action = vm!.loginAction } } class ViewModel { let loginAction: CococaAction = CocoaAction { // do something } } 

这里的问题是你试图让你的“viewModel”成为一个类。 它应该是一个function。

 func viewModel(username: Observable, password: Observable, button: Observable) -> Observable { return button .withLatestFrom(Observable.combineLatest(login, password) { (login, password) }) .flatMap { login, password in server.getAuthToken(withLogin: login, password: password) } .map { Auth(token: $0.token) } 

使用在viewDidLoad中执行此操作来设置它:

 let auth = viewModel(loginTxtField.rx_text, passwordTxtField.rx_text, button.rx_tap) 

如果您的视图模型有多个输出,那么创建一个类可能是值得的(而不是从函数返回一个元组。)如果你想这样做,那么RxSwift repo中的例子中的GithubSignupViewModel1是一个很好的如何设置的示例。