在剛開始接觸RxSwift框架時,最感到迷茫的就是著手用該框架寫出我們自己的項目。個人感覺,在對RAC與RxSwift的基本原理有了一定認識之后,帶著目的與問題去直接動手寫代碼是最快的學習路徑。
這篇先探究一下常用的UI控件在RxSwift框架中,是怎樣運作并最終實現想要的效果
首先是最基本的 lable、textField 與 button 。
下面代碼分別添加了一個 lable、textField 與 button ,布局是用snapKit框架來做的,Objective-C語言環境masonry框架的孿生兄弟。
目的是要在textView輸入的過程中,將輸入信息實時返回給lable.text屬性和button.text屬性,同時還要根據輸入字符長度來改變button是否可用。
//分別初始化一個 button、一個 lable、一個textField,以及一個Rx中的垃圾回收包 DisposeBag
let disposeBag = DisposeBag()
let firstText = UITextField()
let secondLable = UILabel()
let thirdBtn = UIButton(type: .system)
//進行UI布局方法
func createView() -> Void {
firstText.borderStyle = UITextBorderStyle.roundedRect
self.view.addSubview(firstText)
firstText.snp.makeConstraints { (make) in
make.left.equalTo(self.view.snp.left).offset(50)
make.right.equalTo(self.view.snp.right).offset(-50)
make.height.equalTo(44)
make.top.equalTo(self.view.snp.top).offset(80)
}
secondLable.textColor = UIColor.white
secondLable.backgroundColor = UIColor.purple
secondLable.layer.masksToBounds = true
secondLable.layer.cornerRadius = 5;
self.view.addSubview(secondLable)
secondLable.snp.makeConstraints { (make) in
make.top.equalTo(firstText.snp.bottom).offset(40)
make.centerX.equalTo(self.view.snp.centerX)
make.width.equalTo(firstText.snp.width).offset(-50)
make.height.equalTo(50)
}
thirdBtn.setTitle("按鈕", for: UIControlState.normal)
thirdBtn.backgroundColor = UIColor.init(red: 0.8, green: 0.8, blue: 0.8, alpha: 1)
self.view.addSubview(thirdBtn)
thirdBtn.snp.makeConstraints { (make) in
make.top.equalTo(secondLable.snp.bottom).offset(50)
make.centerX.equalTo(secondLable.snp.centerX)
make.width.height.equalTo(secondLable)
}
}
既然是要針對 textField 的 text 屬性來改變其它控件的屬性,那么就將其進行觀察者初始化,因 text 屬性在輸入過程中可能為 nil ,以及增加時間間隔來定期發送信號。
func bindViewModel() -> Void {
//觀察者初始化
let inputTextOb = firstText.rx.text
.orEmpty
.asDriver()
.throttle(0.5) //每隔指定時間發送信號
//將text輸入內容實時返回給lable.text屬性中
inputTextOb.map{ "當前輸入為:\($0)"}
.drive(secondLable.rx.text)
.disposed(by: disposeBag)
//使用map映射判斷text長短是否大于5,將btn是否可用屬性用drive綁定,判斷是否可點擊
inputTextOb.map{$0.count > 5}
.drive(thirdBtn.rx.isEnabled)
.disposed(by: disposeBag)
//作為觀察者序列,將lable的text與btn的title屬性用bind進行綁定
inputTextOb.asObservable()
.bind(to: thirdBtn.rx.title())
.disposed(by: disposeBag)
//btn點擊響應事件
thirdBtn.rx.tap.subscribe(onNext: { [weak self]() in
//打破循環引用
self?.view.backgroundColor = UIColor.blue
print("點擊事件發生")
self?.thirdBtn.backgroundColor = UIColor.red
}).disposed(by: disposeBag)
}
上面在上面代碼中,drive()
方法與 bind()
方法看似同樣實現了將兩個觀察者信號進行綁定的功能,但其中也存在著區別。
drive()
方法只能在 Driver
序列中使用。在初始化觀察者的時候,需要添加 .asDriver()
聲明。
-
Driver
序列不會發送error
信號,也就是訂閱者無法訂閱監聽到 error 信號 -
Driver
序列的訂閱監聽必須在主線程中
這樣的特性,很明顯Driver
就是專門給UI控件量身定做的。 - 在共享事件流的情況下,更適合用
Drive
。比如根據觀察者的屬性變化同時改變多個UI控件狀態
而且,在 button 按鈕點擊的訂閱方法中,改變了 button 自己的屬性,那么在訂閱方法中添加 [weak self]
就可以防止循環引用出現。與RAC中的 @weakily()
作用相同。對應的訂閱方法中所有的 self
持有對象全部變為 self?
最終是下面的效果圖
實現的簡單效果圖
該文章首次發表在 簡書:我只不過是出來寫寫代碼 博客,并自動同步至 騰訊云:我只不過是出來寫寫iOS 博客