ReactiveCocoa使用

西門吹霧發表於2018-01-09

ReactiveCocoa使用
在ReactiveCocoa 5.0之前

看了簡書上的一篇文章,

ReactiveSwift和ReactiveCocoa。

ReactiveSwift

官方原文: ReactiveSwift offers composable, declarative and flexible primitives that are built around the grand concept of streams of values over time. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation.Because all of these different mechanisms can be represented in the same way, it’s easy to declaratively chain and combine them together, with less spaghetti code and state to bridge the gap. 總的來說就是ReactiveSwift提供了可組合的、宣告式的和靈活的基本型別。歸結為以下6大類:

委託方法(Delegate methods) 回撥函式塊(Callback blocks) 通知(Notifications) 控制動作和響應者鏈事件(Control actions and responder chain events) 將來和承諾(Futures and promises) 鍵值觀察(Key-value observing (KVO) 因為這些所有不同的機制都可以用同一種方式來表達,使用基本型別更容易地進行鏈式程式設計,通過把它們結合在一起,減少了套管程式(spaghetti code)。

ReactiveCocoa

官方原文: ReactiveCocoa wraps various aspects of Cocoa frameworks with the declarative ReactiveSwift primitives. ReactiveCocoa使用ReactiveSwift的基本型別來封裝Cocoa框架的方方面面。

介紹完了ReactiveSwift和ReactiveCocoa,下面我們就開始學習吧。

我們要實現 一個登入的頁面,輸入使用者名稱user和password密碼就可以登入啦,登入成功之後

1、開啟你的終端,cd到專案目錄,接著輸入以下命令:

pod init 
複製程式碼

2.文字檔案開啟編輯

target 'RacSwift' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!
        pod 'ReactiveCocoa'
end
複製程式碼

3.儲存退出,cd到根目錄,執行

pod install
複製程式碼

ReactiveCocoa使用
安裝成功

開啟ViewController,匯入import ReactiveCocoa 在viewdidload方法中新增賬戶輸入窗

繪製介面 建立vc成員變數

  var accountTextfield = UITextField();
  var passWordTextfield = UITextField();
  var loginBtn = UIButton();
複製程式碼

在viewdidload初始化

accountTextfield = UITextField.init(frame:CGRect.init(x: 100, y: 200, width: 200, height: 50));
        self.view.addSubview(accountTextfield);
        accountTextfield.placeholder = "輸入賬戶名"
        
        accountTextfield.backgroundColor = UIColor.lightGray;
        passWordTextfield = UITextField.init(frame:CGRect.init(x: 100, y: 300, width: 200, height: 50));
        self.view.addSubview(passWordTextfield);
        passWordTextfield.placeholder = "輸入密碼"
        
        passWordTextfield.backgroundColor = UIColor.lightGray;
        
        loginBtn = UIButton.init(frame: CGRect.init(x: 150, y: 380, width: 100, height: 40))
        
        self.view.addSubview(loginBtn);
        
        loginBtn.backgroundColor = UIColor.gray;
        loginBtn.setTitle("login", for: .normal);
        
        loginBtn.title(for: .normal);
        loginBtn.layer.cornerRadius = 3.0;
        loginBtn.layer.masksToBounds = true;
複製程式碼

新增方法:


        accountTextfield.reactive.continuousTextValues.observeValues { (account) in
            
        };
複製程式碼

執行後輸入Qwertyuiop,控制檯輸出如下:

Q
Qw
Qwe
Qwer
Qwert
Qwerty
Qwertyu
Qwertyui
Qwertyuio
Qwertyuiop

複製程式碼

此時是不是體會到RAC的強大了,我們並沒有新增監聽或者實現委託代理,就可以達到輸入的監聽效果。 這裡簡單分析一下 輸入過程中,ReactiveCocoa訊號會傳送一系列的事件給訂閱者(觀察者)。

ReactiveCocoa有以下事件:

  • Value事件:Value事件提供了新值

  • Failed事件:Failed事件表明在訊號完成之前發生了錯誤

  • Completed事件:Completed事件訊號完成了,之後不會再有新的值傳送

  • Interrupted事件:Interrupted事件表明由於被取消,訊號被終止了

accountTextfield.reactive就是把accountTextfield變成可響應的,而continuousTextValues就是account值的訊號。通過observeValues,我們可以觀察到continuousTextValues這個訊號傳來的Value事件,每次在accountTextfield輸入的時候,我們就會接收到text的值。

使用filter,將程式碼修改如下

 accountTextfield.reactive.continuousTextValues.filter({
            text in
            
            return text!.reversed().count > 4
        }).observeValues {
            text in
            
            print(text ?? "")
        }

複製程式碼

輸入Qwertyuiop,可以看到控制檯輸出

Qwert
Qwerty
Qwertyu
Qwertyui
Qwertyuio
Qwertyuiop
複製程式碼

filter這個函式只允許當它的返回值為true的事件發生,在這裡是輸入的字元數大於4個,也就是說字元數小於等於4的事件會被過濾掉。

繼續新增如下方法

accountTextfield.reactive.continuousTextValues.map({
            text in
            
            return text!.reversed().count
        }).filter({
            characterCount in
            
            return characterCount > 2
        }).observeValues {
            characterCount in
            
            print(characterCount ?? "")
        }
複製程式碼

輸入Qwertyuiop,控制檯輸出:

3
Qwer
4
Qwert
5
Qwerty
6
Qwertyu
7
Qwertyui
8
Qwertyuio
9
Qwertyuiop
10
複製程式碼

新新增的map函式,給map函式提供一個closure,它就能夠轉換事件的資料。對於每一次map接收到的Value事件,它就會執行closure,以closure的返回值作為Value事件傳送出去。上面的程式碼中,我們的text的值對映成text的字元數,這個方法與filter方法通知執行。 加入密碼框

 let passWordTextfield = UITextField.init(frame:CGRect.init(x: 100, y: 300, width: 200, height: 50));
        self.view.addSubview(passWordTextfield);
        passWordTextfield.placeholder = "輸入密碼"
        
        passWordTextfield.backgroundColor = UIColor.lightGray;
複製程式碼

建立合法狀態的訊號

//驗證手機號
 func isAccount()->Bool{
        if accountTextfield.text == account {
            return  true;
        }else{
            
            return false;
        }
    }
// 驗證密碼
    func isPassword()->Bool{
        if passWordTextfield.text == pass {
            return  true;
        }else{
            
            return false;
        }
    }
複製程式碼

在viewdidload新增訊號對映

accountTextfield.reactive.continuousTextValues.map {
            text in
            
            return self.isAccount()
        }

passWordTextfield.reactive.continuousTextValues.map{
            
            text in
            return self.isPassword()
        }
複製程式碼

上述兩個方法已經完成了監聽

let validUsernameSignal = accountTextfield.reactive.continuousTextValues.map({
            text in
            
            return self.isAccount()
        })
        
        validUsernameSignal.map({
            isValidUsername in
            
            return isValidUsername ? UIColor.red : UIColor.yellow
        }).observeValues {
            backgroundColor in
            
            self.accountTextfield.backgroundColor = backgroundColor
        }
        
        let validPasswordSignal = passWordTextfield.reactive.continuousTextValues.map({
            text in
            
            return self.isPassword()
        })
        
        validPasswordSignal.map({
            isValidPassword in
            
            return isValidPassword ? UIColor.red : UIColor.yellow
        }).observeValues {
            backgroundColor in
            
            self.passWordTextfield.backgroundColor = backgroundColor
        }
複製程式碼

同樣的,我們使用map函式把Bool對映成UIColor,然後觀察Value的值,根據Value事件傳來的顏色來改變accountTextfield和passWordTextfield的背景顏色。這樣,當賬號為110或者密碼為110,背景顏色就會高亮成紅色,表示正確,輸入其他變為黃色,表示錯誤。

多個訊號結合在一起

登入按鈕只有在username text field和password text field合法的時候才能被按下去

 let signUpActiveSignal = Signal.combineLatest(validUsernameSignal, validPasswordSignal)
        
        signUpActiveSignal.map({
            (isValidUsername, isValidPassword) in
            
            return isValidUsername && isValidPassword
        }).observeValues {
            signupActive in
            
            self.loginBtn.isEnabled = signupActive
        }
        
複製程式碼

上面的程式碼中我們用Signal(Signal是ReactiveSwift的基本型別,所以我們要import ReactiveSwift)的Signal.combineLatest方法將validUsernameSignal和validPasswordSignal兩個訊號結合在一起,再將它們對映成一個Bool訊號來表明username text field和password text field是否同時合法。

之後,通過觀察Value事件,我們將訊號傳過來的值賦值給loginBtn。這樣,loginBtn的可用性就可以通過訊號來控制了。

在ReactiveSwift中,你可以做到更酷,把上面程式碼替換成:

 loginBtn.reactive.isEnabled <~ Signal.combineLatest(validUsernameSignal, validPasswordSignal).map { $0 && $1 }
複製程式碼

上面的程式碼展示了ReactiveCocoa的強大之處:

可分開的(Splitting):訊號可用擁有多個訂閱者(觀察者),來作為後續步驟的訊號源。注意到validUsernameSignal和validPasswordSignal是兩個用來驗證username text field和password text field分開的合法的訊號,這兩個訊號有著不同的目的。 可結合的(Combining):多個訊號可以結合在一起來建立一個新的訊號。更值得興奮的是,你可以結合任意型別的訊號來建立新的訊號。

之前我們是直接給loginBtn新增方法

loginBtn.addTarget(self, action: #selector(loginBtnAction), for: .touchUpInside)
複製程式碼
 @objc  func loginBtnAction()  {
        print("login");
    
}
複製程式碼

我們可以直接使用controlEvents

  
    
        let signInSignal = loginBtn.reactive.controlEvents(.touchUpInside)
        
        signInSignal.observeValues {_ in
            print("button clicked")
        }  
複製程式碼

相關文章