什麼是雙向綁定?雙向綁定是View與Model之間的一種相互響應的關係。 git
![]()
下面👇以一個Demo實戰來體驗一下雙向綁定帶來的快感!!!github
Demo要實現的功能比較簡單:在搜索框中輸入搜索內容 -> 發起網絡請求獲取搜索結果 -> 在tableView中展現搜索結果。 PS:UI代碼請小夥伴隨意腦補,就不贅述啦json
在UI搭建好以後,須要建立一個ViewModel類,來響應UI的變化。swift
class BOViewModel: NSObject {
// 響應SearchBar的輸入
let searchTextOB = BehaviorSubject(value: "")
}
複製代碼
添加屬性 searchTextOB
來響應輸入框的輸入變化,同時爲了能在收到響應後,發起網絡請求,因此使用了 BehaviorSubject
類型。畢竟,Subject 類型能夠是序列,也能夠是觀察者。api
class TBViewController: UIViewController {
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
let viewModel = BOViewModel()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// 實現 view -綁定-> model
self.searchBar.rx.text.orEmpty
.bind(to: self.viewModel.searchTextOB)
.disposed(by: self.disposeBag)
}
}
複製代碼
使用 bind
函數,完成輸入框綁定到Model。數組
lazy var searchData: Driver<[BOReposityModel]> = {
}()
複製代碼
爲了只產生一個可觀察序列,因此使用懶加載。不須要錯誤處理,因此使用 Driver
對象。網絡
在懶加載內部,將 searchTextOB
做爲可觀察序列訂閱。即在 searchTextOB
收到外界輸入框的輸入變化時,同步發送信號,發起網絡請求。閉包
lazy var searchData: Driver<[BOReposityModel]> = {
return self.searchTextOB.asObservable()
// 每隔300毫秒請求一直,而且在主線程發起請求
.throttle(0.3, scheduler: MainScheduler.instance)
// 阻止發送相同的信號
.distinctUntilChanged()
.flatMapFirst(BOViewModel.requestData)
// 若是出錯,則返回空數組
.asDriver(onErrorJustReturn: [])
}()
複製代碼
flatMapFirst
函數接收一個閉包做爲參數,但其實閉包也是一個函數,因此這裏傳入一個函數做爲參數也是能夠的。本例中使用的請求地址爲:api.github.com/users/(gith…框架
在發起網絡請求以前須要先判斷傳入的參數是否爲空,已經網絡地址是否正確。若是有錯誤,則返回空數組。異步
static func requestData(_ githubId: String) -> Observable<[BOReposityModel]> {
guard !githubId.isEmpty, let url = URL(string: "https://api.github.com/users/\(githubId)/repos") else {
return Observable.just([])
}
return URLSession.shared.rx.json(url: url)
.retry()
.observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.map(BOViewModel.parseData)
}
複製代碼
使用 URLSession
發起異步網絡請求,而且對請求的結果進行數據解析。
class BOReposityModel: HandyJSON {
var name: String = ""
var url: String = ""
required init() {}
}
複製代碼
static func parseData(_ json: Any) -> [BOReposityModel] {
guard let datas = json as? [[String: Any]] else { return [] }
guard let result = [BOReposityModel].deserialize(from: datas) else { return [] }
return result as! [BOReposityModel]
}
複製代碼
這裏使用了一個三方庫 HandyJSON 進行數據解析,小夥伴也能夠根據本身的喜愛和習慣,選擇本身熟悉的數據解析方式。
請求到數據之後,就須要將數據展現到頁面上。
一、展現搜索結果數量到導航欄上
// 實現 model -綁定-> view
self.viewModel.searchData.map { (array) -> String in
return "共\(array.count)個結果"
}
.drive(self.navigationItem.rx.title)
.disposed(by: self.disposeBag)
複製代碼
二、展現結果到tableView列表上
// 實現 model -綁定-> view
self.viewModel.searchData.drive(self.tableView.rx.items){
(tableView, row, model) in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! BOTableViewCell
cell.nameLabel.text = model.name
cell.urlLabel.text = model.url
return cell
}
.disposed(by: self.disposeBag)
複製代碼
searchData
做爲請求結果序列,直接驅動 tableView 的顯示刷新。
附個demo圖:UI太醜,勿噴
使用這樣的方式,無需再實現 UITableView 的 delegate 和 dataSource 協議方法。是否是簡單不少?
RxSwift已經幫咱們封裝好了UITableView的代理協議,因此咱們能夠更專一於業務層以及UI層面,而再也不須要各類冗餘的代碼。
使用 RxSwift 雙向綁定來實現簡單的搜 Demo,ViewController 中的代碼不過區區60行左右,若是使用非RxSwift的方式,代碼至少百行。這僅僅是計算Controller中的代碼,還不包括ViewModel。這麼好用的框架此時不用,更待什麼時候?
好的框架須要學習,好的技術須要實戰。