簡體   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