RXSwift--登錄注冊那點事

在iOS學(xué)習(xí)中登錄注冊是一個萬能的可以拿出來實戰(zhàn)的demo。接下來我們就從登錄開始入手,PS:如果你對RXSwift中的概念和一些常用的函數(shù)不清楚可以參考這篇文章(可能打開比較慢請耐心等待)。開始直接上代碼。先看一下我們要實現(xiàn)的效果。

loginGif.gif

分析實現(xiàn):
1.在還沒有輸入的時候,顯示提醒信息
2.輸入賬號和密碼正確的時候隱藏提示信息
2.在賬號和密碼都輸入的時候登錄按鈕可以點擊

1.直接在storyBoard中創(chuàng)建簡單的登錄界面

簡單的登錄界面

2.關(guān)聯(lián)好對應(yīng)的屬性,接下來引入我們今天的重點對象

import RxSwift
import RxCocoa

創(chuàng)建一個disposeBag來盛放我們需要管理的資源,然后把新建的監(jiān)聽都放進(jìn)去,會在適當(dāng)?shù)臅r候銷毀這些資源。
let disposeBag = DisposeBag()

3.接下來開始對事件的判斷和綁定事件

        //判斷賬號的輸入是否可用
        let accountValid:Observable = accountField.rx.text.orEmpty.map{ value in
            return value.characters.count >= 6
        }
        //判斷密碼的輸入是否可用
        let passwordValid:Observable = passwordField.rx.text.orEmpty.map{ value in
            return value.characters.count >= 6
        }

上面orEmpty是判斷當(dāng)前字符串是否為空的,在RXSwift中已經(jīng)處理了為nil的情況,map函數(shù)是在事件流轉(zhuǎn)換的時候,重新生成另一個事件流,在這里是把一個文字事件流映射成一個bool事件流,accountValidpasswordValid都是Observable<Bool>類型

對于賬號和密碼輸入正確與否的一個顯示

//賬號密碼輸入的正確與否 綁定到infoLabel的hidden屬性上
//綁定顯示
      accountValid.bind(to: accountInfoLabel.rx.isHidden).addDisposableTo(disposeBag)
      passwordValid.bind(to: passwordInfoLabel.rx.isHidden).addDisposableTo(disposeBag)

接著就是對于登錄按鈕的是否可點擊的綁定

        //登錄按鈕的可用與否
        let loginObserver = Observable.combineLatest(accountValid,passwordValid){(account,password) in
            account && password
        }
        //綁定按鈕
        loginObserver.bind(to: loginBtn.rx.isEnabled).addDisposableTo(disposeBag)
        loginObserver.subscribe(onNext: { [unowned self] valid in
            self.loginBtn.alpha = valid ? 1 : 0.5
        }).disposed(by: disposeBag)

上面的將賬號和密碼輸入的值與按鈕的enable屬性相關(guān)聯(lián),當(dāng)accountValidtrue,并且passwordValid也為true時,按鈕才可點擊,同時也修改了按鈕的透明度變化

接下來就是按鈕點擊事件的判斷以及對應(yīng)的方法的執(zhí)行

loginBtn.rx.tap
            .asObservable()
            .withLatestFrom(loginObserver)
            .do(onNext: {
                [unowned self]_ in
                self.loginBtn.isEnabled = false
                self.view.endEditing(true)
            })
            .subscribeOn(MainScheduler.instance)//主線程
            .subscribe(onNext: {[unowned self]isLogin in
                self.showAlert(message: "開始點擊")
                self.loginBtn.isEnabled = true
            })
            .addDisposableTo(disposeBag)//開始釋放

按鈕的點擊事件中綁定的是loginObserver最新的一個流操作,do(onNext)函數(shù)是在執(zhí)行之前對按鈕的一個限定,比如網(wǎng)絡(luò)請求延遲,按鈕點擊多次,我在按鈕第一次點擊的時候,就禁用按鈕,等到網(wǎng)絡(luò)請求成功或者失敗返回信息的時候再修改按鈕可點擊的狀態(tài),.subscribeOn函數(shù)是指定事件流在那個線程中執(zhí)行,這里指定的是主線程。subscribe(onNext…………這是點擊按鈕之后執(zhí)行方法的閉包。 簡寫也可以寫成這個樣子哦,這個只是簡單處理按鈕的點擊事件

loginBtn.rx.tap
            .subscribe(onNext: {[unowned self]isLogin in
                self.showAlert(message: "開始點擊")
            })
            .addDisposableTo(disposeBag)//開始釋放

最后是alertView的一個彈出視圖

fileprivate func showAlert(message:String) {
        let action = UIAlertAction.init(title: "確定", style: .default, handler: nil)
        let alertView = UIAlertController.init(title: nil, message: message, preferredStyle: .alert)
        alertView.addAction(action)
        present(alertView, animated: true, completion: nil)
    }

以上只是一個簡單的值綁定進(jìn)行的判斷,接下來我們要使用Observable和Driver去實現(xiàn)這個登錄注冊功能,下面實現(xiàn)的比較繞,請坐好車

接下來我們要使用Driver去實現(xiàn)登錄功能。先說明一下Observable和Driver的一個簡介。

RXSwift

RxSwift的核心是想是 Observable<Element> sequenceObservable表示可監(jiān)聽或者可觀察,也就是說RxSwift的核心思想是可監(jiān)聽的序列。并且,Observable sequence可以接受異步信號,也就是說,信號是可以異步給監(jiān)聽者的

  • Observable(ObservableType) 和 SequenceType類似
  • ObservableType.subscribe 和 SequenceType.generate類似
  • 由于RxSwift支持異步獲得信號,所以用ObservableType.subscribe,這和indexGenerator.next()類似

本文把RxSwift中的序列的每一個Element成為信號,因為異步的Element是與時間相關(guān)的,稱作信號更好理解一點。

Driver

Driver是RxSwift精心制作的,專門提供給UI層的一個接口。
利用Driver你可以

  • 利用CoreData的模型來驅(qū)動UI
  • 利用UI的狀態(tài)來綁定其他UI的狀態(tài)
    Driver能夠保證,在主線程上監(jiān)聽,因為UIKit不是需要在主線程上操作

使用Driver時UI布局和上面一樣都是一個簡單的登錄界面,接下來我們使用MVVM來構(gòu)建一個登錄界面的邏輯處理。

1.新建一個Service類處理用戶名,密碼和登錄按鈕的狀態(tài) ,新建一個Model類處理綁定事件

首先是Service類的一個創(chuàng)建,用戶輸入賬號的時候有三種狀態(tài)

enum Result {
    case ok(message:String)//輸入正確
    case empty//輸入為空
    case failed(message:String)//輸入不合法
}

用這三種狀態(tài)去判斷所輸入的賬號和密碼是否是合法的

    static let instance = ValidationService() // 定義一個單例
    let minCharactersCount = 6 //最少字符限制
    private init(){}
    
    //返回一個Observable對象,這個請求過程要被監(jiān)聽
    //MARK: 登錄用戶名驗證
    func LoginUserNameValid(_ userName:String) -> Observable<Result> {
        if userName.characters.count == 0 {
            return .just(.empty);
        }
        
        if userName.characters.count < minCharactersCount {
            return .just(.failed(message: "用戶名至少是6個字符"))
        }
        
        return .just(.ok(message:"用戶名可用"))
    }
    
    func LoginPasswordValid(_ password:String) -> Observable<Result> {
        if password.characters.count == 0 {
            return .just(.empty)
        }
        
        if password.characters.count < minCharactersCount {
            return .just(.failed(message:"密碼長度至少6個字符"))
        }
        
        return .just(.ok(message:"密碼可用"))
    }

    //開始登錄,定義的一個登錄事件,在這里面進(jìn)行網(wǎng)絡(luò)回調(diào)
    func login(_ userName:String,password:String) -> Observable<Result> {
           //根據(jù)網(wǎng)絡(luò)返回的數(shù)據(jù)進(jìn)行 返回
            if userName.characters.count > 0 && password.characters.count > 0{
                return .just(.ok(message:"登錄成功"))
            }
           return .just(.failed(message:"密碼或登錄名錯誤"))
    }

2.接下來就是Model類

創(chuàng)建一個swift文件,在類中聲明

    //輸出 這是輸出的一個定義
    let userNameUsable:Driver<Result>
    let userPasswordAble:Driver<Result>
    let loginButtonEnabled :Driver<Bool>
    let loginResult:Driver<Result>

初始化函數(shù)如下

    init(input:(userName:Driver<String>,password:Driver<String>,loginTaps:Driver<Void>),service:ValidationService) {
        //用戶名是否合法
        userNameUsable = input.userName
                              .flatMapLatest{ username  in
                                  return service.LoginUserNameValid(username)
                                                .asDriver(onErrorJustReturn: .failed(message: "連接服務(wù)失敗"))}
        //密碼是否合法
        userPasswordAble = input.password
            .flatMapLatest{ password in
            return service.LoginPasswordValid(password)
                .asDriver(onErrorJustReturn: .failed(message: "密碼填寫錯誤"))
        }
        
        let userNameAndPassword = Driver.combineLatest(input.userName,input.password){($0,$1)}
        //按鈕點擊的觸發(fā)事件
        loginResult = input.loginTaps
            .withLatestFrom(userNameAndPassword)
            .flatMapLatest{ (arg) -> SharedSequence<DriverSharingStrategy, Result> in
                let (userName, password) = arg
                return service.login(userName, password: password).asDriver(onErrorJustReturn: .failed(message:"連接服務(wù)失敗"))
        }
       //按鈕是否可以點擊
        loginButtonEnabled = input.password
                                  .map{$0.characters.count > 0}
                                  .asDriver()
    }

3.在ViewController中初始化model類 進(jìn)行事件的綁定

       let viewModel = LoginViewModel.init(
            input: (
            userName: accountField.rx.text.orEmpty.asDriver(), 
            password: passwordField.rx.text.orEmpty.asDriver(),
            loginTaps: loginBtn.rx.tap.asDriver()), 
            service: ValidationService.instance
            )

        viewModel.userNameUsable
            .drive(accountInfoLabel.rx.validationResult)
            .addDisposableTo(disposeBag)

        viewModel.userPasswordAble
            .drive(passwordInfoLabel.rx.validationResult)
            .addDisposableTo(disposeBag)

        viewModel.loginButtonEnabled
            .drive(onNext: { [unowned self] valid in
            self.loginBtn.isEnabled = valid
            self.loginBtn.alpha = valid ? 1 : 0.5
            })
            .addDisposableTo(disposeBag)

        viewModel.loginResult
            .drive(onNext: { [unowned self] result in
                switch result{
                case .empty:
                    self.showAlert(message: "")
                case let .ok(message):
                    print(message)
                    //開始進(jìn)行跳轉(zhuǎn)
                    self.showAlert(message: message)
                case let .failed(message):
                    self.showAlert(message: message)
                }
        })
        .addDisposableTo(disposeBag)

實現(xiàn)效果如下

DriverLogin.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容