看了簡書上的一篇文章,
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
複製程式碼
安裝成功
開啟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")
}
複製程式碼