一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情。
- 本文主要介绍
下RxDataSources
对于RxSwift 也有很多拓展,我们比较常用的就是RxCocoa
,对于Tableview
或者collectionView
也有封装,里面还有其他的拓展,感兴趣的可以看看 RxSwiftCommunity)
1. RxCocoa
我们正常写的话就是初始化,签订代理,实现代理。
那么使用RxCocoa怎么实现呢?我们展示的话
let items = Observable.just([
"First Item",
"Second Item",
"Third Item"
])
items
.bind(to: tableView.rx.items) { (tableView, row, element) in
let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell")!
cell.textLabel?.text = "\(element) @ row \(row)"
return cell
}
.disposed(by: disposeBag)
不需要签订协议,直接绑定到我们的tableview上,这里我们也可以自己注册cell
tableView.rx.items
items
.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
cell.textLabel?.text = "\(element) @ row \(row)"
}
.disposed(by: disposeBag)
对于我们tableView的一些代理事件也进行了封装
//点击事件
tableView.rx.itemSelected
.subscribe { indexPath in
print("点击了\(indexPath.element!.row)")
}.disposed(by: disposeBag)
但是我们对于有的比如返回tableView的行高等,就没有封装,我们这时候可以设置delegate
来实现
tableView.rx.setDelegate(self)
.disposed(by: disposeBag)
我们实现具体代理
对于一些复杂的分组等,我们可以使用 RxDataSources
2. RxDataSources
我们先看下简单的分组使用首先我们看下sectionModel
当然我们也可以自己自定义
struct MySection {
var header: String
var items: [Item]
}
extension MySection : AnimatableSectionModelType {
typealias Item = Int
var identity: String {
return header
}
init(original: MySection, items: [Item]) {
self = original
self.items = items
}
}
我们创建datasource
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
let dataSource1 = RxTableViewSectionedAnimatedDataSource<MySection>(
configureCell: { ds, tv, _, item in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
cell.textLabel?.text = "Item \(item)"
return cell
},
titleForHeaderInSection: { ds, index in
return ds.sectionModels[index].header
}
)
self.dataSource = dataSource1
let sections = [
MySection(header: "First section", items: [
1,
2
]),
MySection(header: "Second section", items: [
3,
4
])
]
Observable.just(sections)
.bind(to: tableView.rx.items(dataSource: dataSource1))
.disposed(by: disposeBag)
运行效果
- 不同数据源的分组
enum MultipleSectionModel {
case ImageProvidableSection(title: String, items: [SectionItem])
case ToggleableSection(title: String, items: [SectionItem])
case StepperableSection(title: String, items: [SectionItem])
}
enum SectionItem {
case ImageSectionItem(image: UIImage, title: String)
case ToggleableSectionItem(title: String, enabled: Bool)
case StepperSectionItem(title: String)
}
extension MultipleSectionModel: SectionModelType {
typealias Item = SectionItem
var items: [SectionItem] {
switch self {
case .ImageProvidableSection(title: _, items: let items):
return items.map { $0 }
case .StepperableSection(title: _, items: let items):
return items.map { $0 }
case .ToggleableSection(title: _, items: let items):
return items.map { $0 }
}
}
init(original: MultipleSectionModel, items: [Item]) {
switch original {
case let .ImageProvidableSection(title: title, items: _):
self = .ImageProvidableSection(title: title, items: items)
case let .StepperableSection(title, _):
self = .StepperableSection(title: title, items: items)
case let .ToggleableSection(title, _):
self = .ToggleableSection(title: title, items: items)
}
}
}
这里使用枚举展示定义不同的cell样式,和Item。我们处理数据dataSource
static func dataSource() -> RxTableViewSectionedReloadDataSource<MultipleSectionModel> {
return RxTableViewSectionedReloadDataSource<MultipleSectionModel>(
configureCell: { dataSource, table, idxPath, _ in
switch dataSource[idxPath] {
case let .ImageSectionItem(image, title):
let cell: ImageTitleTableViewCell = table.dequeueReusableCell(forIndexPath: idxPath)
cell.configure(image: image, title: title)
return cell
case let .StepperSectionItem(title):
let cell: TitleSteperTableViewCell = table.dequeueReusableCell(forIndexPath: idxPath)
cell.configure(title: title)
return cell
case let .ToggleableSectionItem(title, enabled):
let cell: TitleSwitchTableViewCell = table.dequeueReusableCell(forIndexPath: idxPath)
cell.configure(title: title, isEnabled: enabled)
return cell
}
},
titleForHeaderInSection: { dataSource, index in
let section = dataSource[index]
return section.title
}
)
}
}
我们tableView绑定我们datasource
let sections: [MultipleSectionModel] = [
.ImageProvidableSection(title: "Section 1",
items: [.ImageSectionItem(image: UIImage(named: "settings")!, title: "General")]),
.ToggleableSection(title: "Section 2",
items: [.ToggleableSectionItem(title: "On", enabled: true),.ToggleableSectionItem(title: "Off", enabled: false)]),
.StepperableSection(title: "Section 3",
items: [.StepperSectionItem(title: "1"),.StepperSectionItem(title: "2"),.ToggleableSectionItem(title: "On", enabled: true)])
]
let dataSource = ViewController.dataSource()
Observable.just(sections)
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
运行效果
3. 对MJ封装
我们通常也会对上拉刷新和下拉加载更多进行分装,把我们刷新的事件转换为序列,并提供绑定。
extension Reactive where Base: UITableView {
/// 刷新
var refresh: ControlEvent<RefreshType> {
let source = Observable<RefreshType>.create { observer in
if base.mj_header == nil {
base.mj_header = MJRefreshNormalHeader(refreshingBlock: {
observer.on(.next(.refresh))
})
}
if base.mj_footer == nil {
base.mj_footer = MJRefreshAutoNormalFooter(refreshingBlock: {
observer.on(.next(.loadMore))
})
}
return Disposables.create()
}
return ControlEvent(events: source)
}
/// 停止下拉刷新
var endRefresh: Binder<(RefreshType, Bool)> {
Binder(base) { control, data in
switch data.0 {
case .refresh:
control.mj_header?.endRefreshing()
if data.1 {
control.mj_footer?.endRefreshingWithNoMoreData()
} else {
control.mj_footer?.resetNoMoreData()
}
case .loadMore:
control.mj_footer?.endRefreshing()
if data.1 {
control.mj_footer?.endRefreshingWithNoMoreData()
}
default:
if data.1 {
control.mj_footer?.endRefreshingWithNoMoreData()
}
}
}
}
}
其中RefreshType我们定义为刷新类型
enum RefreshType {
case none
case refresh
case loadMore
}
4. 总结
对于一些简单的展示类的cell比较适合,但是对于一些负责的tableView,拓展性不是很好,比如cell高度的设定,当然我们自己也可以拓展。看自己需要,灵活运用。