本文是官方案例GitHubSignup-UsingDriver學習筆記
專案實現功能
這個登入頁面實現了下面幾個功能: 1.檢驗使用者名稱是否可用 2.密碼是否符合要求 3.確認密碼是符合密碼一樣 4.上面上面三個都符合要求,登入按鈕才可以點選 5.當使用者正在登入的時候,顯示 activityIndicator,提醒使用者等待,此時按鈕不能被按;當得到登入結果的時候,隱藏 activityIndicator。 6.登入完成顯示登入結果
具體實現
GitHubSignupViewController2.swift
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
複製程式碼
引用部分RX_NO_MODULE這個巨集的字面意思應該是沒有rx的模組意思,但是沒有找到具體實現在哪裡,如果誰知道麻煩告知
@IBOutlet weak var usernameOutlet: UITextField!
@IBOutlet weak var usernameValidationOutlet: UILabel!
@IBOutlet weak var passwordOutlet: UITextField!
@IBOutlet weak var passwordValidationOutlet: UILabel!
@IBOutlet weak var repeatedPasswordOutlet: UITextField!
@IBOutlet weak var repeatedPasswordValidationOutlet: UILabel!
@IBOutlet weak var signupOutlet: UIButton!
@IBOutlet weak var signingUpOulet: UIActivityIndicatorView!
複製程式碼
首先是各個控制元件的繫結
let viewModel = GithubSignupViewModel2(
input: (
username: usernameOutlet.rx.text.orEmpty.asDriver(),
password: passwordOutlet.rx.text.orEmpty.asDriver(),
repeatedPassword: repeatedPasswordOutlet.rx.text.orEmpty.asDriver(),
loginTaps: signupOutlet.rx.tap.asDriver()
),
dependency: (
API: GitHubDefaultAPI.sharedAPI,
validationService: GitHubDefaultValidationService.sharedValidationService,
wireframe: DefaultWireframe.sharedInstance
)
)
複製程式碼
viewModel初始化,rx是RxSwift的域名,text是觀察的屬性,orEmpty是檢驗text是否為nil如果為nil返回"",asDriver()是具體特殊屬性的Observable,如果使用asObservable(),就要額外新增.observeOn(MainScheduler.instance)和.shareReplay(1), Driver是屬於Rxcocoa庫,是對Observable進行了一些封裝,Observable是屬於RxSwift庫
viewModel.signupEnabled
.drive(onNext: { [weak self] valid in
self?.signupOutlet.isEnabled = valid
self?.signupOutlet.alpha = valid ? 1.0 : 0.5
})
.addDisposableTo(disposeBag)
複製程式碼
監聽viewModel.signupEnabled的值變化,signupEnabled的型別為 Driver,那麼valid的型別就為Bool,根據valid來設定按鈕的狀態
viewModel.validatedUsername
.drive(usernameValidationOutlet.rx.validationResult)
.addDisposableTo(disposeBag)
viewModel.validatedPassword
.drive(passwordValidationOutlet.rx.validationResult)
.addDisposableTo(disposeBag)
viewModel.validatedPasswordRepeated
.drive(repeatedPasswordValidationOutlet.rx.validationResult)
.addDisposableTo(disposeBag)
viewModel.signingIn
.drive(signingUpOulet.rx.isAnimating)
.addDisposableTo(disposeBag)
複製程式碼
將viewModel.validatedUsername和UILabel的validationResult繫結, validationResult是UILabel自定義的Rx擴充套件,原始碼如下:
extension Reactive where Base: UILabel {
var validationResult: UIBindingObserver<Base, ValidationResult> {
return UIBindingObserver(UIElement: base) { label, result in
label.textColor = result.textColor
label.text = result.description
}
}
}
複製程式碼
let tapBackground = UITapGestureRecognizer()
tapBackground.rx.event
.subscribe(onNext: { [weak self] _ in
self?.view.endEditing(true)
})
.addDisposableTo(disposeBag)
view.addGestureRecognizer(tapBackground)
複製程式碼
以上是新建一個tap手勢並使用Rx對手勢的監聽來實現相應的功能
GithubSignupViewModel2.swift
init(
input: (
username: Driver<String>,
password: Driver<String>,
repeatedPassword: Driver<String>,
loginTaps: Driver<Void>
),
dependency: (
API: GitHubAPI,
validationService: GitHubValidationService,
wireframe: Wireframe
)
)
複製程式碼
首先是初始化接受一個兩個元組作為引數
validatedUsername = input.username
.distinctUntilChanged() //demo重複檢查
.flatMapLatest { username in
return validationService.validateUsername(username)
.asDriver(onErrorJustReturn: .failed(message: "Error contacting server"))
}
validatedPassword = input.password
.map { password in
return validationService.validatePassword(password)
}
複製程式碼
對username和password的值進行處理,然後返回Driver的結果為後面處理做準備
validatedPasswordRepeated = Driver.combineLatest(input.password, input.repeatedPassword, resultSelector: validationService.validateRepeatedPassword)
複製程式碼
Driver.combineLatest將兩個流合併成一個流,通過對原始碼的閱讀發現combineLatest最多支援8路流合併成一路流, resultSelector提供多路流合併的方法,這裡可以寫成閉包的形式,也可以直接傳入一個處理函式.
let signingIn = ActivityIndicator()
複製程式碼
ActivityIndicator提供檢測網路訪問狀態的方法
self.signingIn = signingIn.asDriver()
複製程式碼
提供網路訪問的狀態監聽
.trackActivity(signingIn)
複製程式碼
在網路請求時新增到上面的方法,可以監聽網路狀態,網路開始訪問時返回true,網路訪問結束時返回false
signedIn = input.loginTaps.withLatestFrom(usernameAndPassword)
.flatMapLatest { (username, password) in
return API.signup(username, password: password)
.trackActivity(signingIn)
.asDriver(onErrorJustReturn: false)
}
.flatMapLatest { loggedIn -> Driver<Bool> in
let message = loggedIn ? "Mock: Signed in to GitHub." : "Mock: Sign in to GitHub failed"
return wireframe.promptFor(message, cancelAction: "OK", actions: [])
// propagate original value
.map { _ in
loggedIn
}
.asDriver(onErrorJustReturn: false)
}
複製程式碼
每次登入按鈕點選的時候,利用.withLatestFrom(usernameAndPassword)從usernameAndPassword中獲取使用者名稱和密碼,然後傳給網路請求進行訪問,然後顯示登入結果,並返回登入結果給上層處理
signupEnabled = Driver.combineLatest(
validatedUsername,
validatedPassword,
validatedPasswordRepeated,
signingIn
) { username, password, repeatPassword, signingIn in
username.isValid &&
password.isValid &&
repeatPassword.isValid &&
!signingIn
}
.distinctUntilChanged()
複製程式碼
通過validatedUsername, validatedPassword, validatedPasswordRepeated, signingIn四個事件流的狀態來決定signupEnabled的狀態,也就是決定登入按鈕的狀態