什么是双向绑定?双向绑定是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。这么好用的框架此时不用,更待什么时候?
好的框架须要学习,好的技术须要实战。