RXSwift--登錄注冊那點事

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

loginGif.gif

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

1.直接在storyBoard中創建簡單的登錄界面

簡單的登錄界面

2.關聯好對應的屬性,接下來引入我們今天的重點對象

import RxSwift
import RxCocoa

創建一個disposeBag來盛放我們需要管理的資源,然后把新建的監聽都放進去,會在適當的時候銷毀這些資源。
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是判斷當前字符串是否為空的,在RXSwift中已經處理了為nil的情況,map函數是在事件流轉換的時候,重新生成另一個事件流,在這里是把一個文字事件流映射成一個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屬性相關聯,當accountValidtrue,并且passwordValid也為true時,按鈕才可點擊,同時也修改了按鈕的透明度變化

接下來就是按鈕點擊事件的判斷以及對應的方法的執行

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)函數是在執行之前對按鈕的一個限定,比如網絡請求延遲,按鈕點擊多次,我在按鈕第一次點擊的時候,就禁用按鈕,等到網絡請求成功或者失敗返回信息的時候再修改按鈕可點擊的狀態,.subscribeOn函數是指定事件流在那個線程中執行,這里指定的是主線程。subscribe(onNext…………這是點擊按鈕之后執行方法的閉包。 簡寫也可以寫成這個樣子哦,這個只是簡單處理按鈕的點擊事件

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)
    }

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

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

RXSwift

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

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

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

Driver

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

  • 利用CoreData的模型來驅動UI
  • 利用UI的狀態來綁定其他UI的狀態
    Driver能夠保證,在主線程上監聽,因為UIKit不是需要在主線程上操作

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

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

首先是Service類的一個創建,用戶輸入賬號的時候有三種狀態

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

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

    static let instance = ValidationService() // 定義一個單例
    let minCharactersCount = 6 //最少字符限制
    private init(){}
    
    //返回一個Observable對象,這個請求過程要被監聽
    //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:"密碼可用"))
    }

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

2.接下來就是Model類

創建一個swift文件,在類中聲明

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

初始化函數如下

    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: "連接服務失敗"))}
        //密碼是否合法
        userPasswordAble = input.password
            .flatMapLatest{ password in
            return service.LoginPasswordValid(password)
                .asDriver(onErrorJustReturn: .failed(message: "密碼填寫錯誤"))
        }
        
        let userNameAndPassword = Driver.combineLatest(input.userName,input.password){($0,$1)}
        //按鈕點擊的觸發事件
        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:"連接服務失敗"))
        }
       //按鈕是否可以點擊
        loginButtonEnabled = input.password
                                  .map{$0.characters.count > 0}
                                  .asDriver()
    }

3.在ViewController中初始化model類 進行事件的綁定

       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)
                    //開始進行跳轉
                    self.showAlert(message: message)
                case let .failed(message):
                    self.showAlert(message: message)
                }
        })
        .addDisposableTo(disposeBag)

實現效果如下

DriverLogin.gif
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容