一、基本使用

① 单分区的表格

  • 如下所示,单个分区的表格展示:

  • 示例代码:
import UIKit
import RxSwift
import RxCocoaclass ViewController: UIViewController {var tableView:UITableView!let disposeBag = DisposeBag()override func viewDidLoad() {super.viewDidLoad()// 创建表格视图self.tableView = UITableView(frame: self.view.frame, style:.plain)// 创建一个重用的单元格self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")self.view.addSubview(self.tableView!)// 初始化数据let items = Observable.just(["文本输入框的用法","开关按钮的用法","进度条的用法","文本标签的用法",])// 设置单元格数据(其实就是对 cellForRowAt 的封装)items.bind(to: tableView.rx.items) { (tableView, row, element) inlet cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "\(row):\(element)"return cell}.disposed(by: disposeBag)}
}

② 单元格选中事件响应

  • 当点击某个单元格时将其索引位置,以及对应的标题打印出来:

选中项的indexPath为:[0, 3]
选中项的标题为:文本标签的用法
  • 业务代码直接放在响应方法内部,示例代码:
// 获取选中项的索引
tableView.rx.itemSelected.subscribe(onNext: { indexPath inprint("选中项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)// 获取选中项的内容
tableView.rx.modelSelected(String.self).subscribe(onNext: { item inprint("选中项的标题为:\(item)")
}).disposed(by: disposeBag)
  • 也可以在响应中调用外部的方法:
// 获取选中项的索引
tableView.rx.itemSelected.subscribe(onNext: { [weak self] indexPath inprint("选中项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)// 获取选中项的内容
tableView.rx.modelSelected(String.self).subscribe(onNext: {[weak self] item inprint("选中项的标题为:\(item)")
}).disposed(by: disposeBag)

③ 单元格取消选中事件响应

被取消选中项的indexPath为:[0, 2]
被取消选中项的的标题为:进度条的用法
  • 示例代码:
// 获取被取消选中项的索引
tableView.rx.itemDeselected.subscribe(onNext: { [weak self] indexPath inprint("被取消选中项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)// 获取被取消选中项的内容
tableView.rx.modelDeselected(String.self).subscribe(onNext: {[weak self] item inprint("被取消选中项的的标题为:\(item)")
}).disposed(by: disposeBag)

④ 单元格删除事件响应

  • 如下所示,左滑删除:
  • 示例代码:
// 获取删除项的索引
tableView.rx.itemDeleted.subscribe(onNext: { indexPath inprint("删除项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)// 获取删除项的内容
tableView.rx.modelDeleted(String.self).subscribe(onNext: {item inprint("删除项的的标题为:\(item)")
}).disposed(by: disposeBag)

⑤ 单元格移动事件响应

移动项原来的indexPath为:[0, 0]
移动项现在的indexPath为:[0, 1]
  • 示例代码:
// 获取移动项的索引
tableView.rx.itemMoved.subscribe(onNext: {sourceIndexPath, destinationIndexPath inprint("移动项原来的indexPath为:\(sourceIndexPath)")print("移动项现在的indexPath为:\(destinationIndexPath)")
}).disposed(by: disposeBag)

⑥ 单元格插入事件响应

插入项的indexPath为:[0, 1]
  • 示例代码:
// 获取插入项的索引
tableView.rx.itemInserted.subscribe(onNext: { indexPath inprint("插入项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)

⑦ 单元格尾部附件(图标)点击事件响应

尾部项的indexPath为:[0, 1]
  • 示例代码:
// 获取点击的尾部图标的索引
tableView.rx.itemAccessoryButtonTapped.subscribe(onNext: { indexPath inprint("尾部项的indexPath为:\(indexPath)")
}).disposed(by: disposeBag)

⑧ 单元格将要显示出来的事件响应

  • 如下所示:
将要显示单元格indexPath为:[0, 0]
将要显示单元格cell为:<UITableViewCell: 0x7fe74e925fd0; frame = (0 0; 428 45); text = '0:文本输入框的用法'; autoresize = W; layer = <CALayer: 0x600003d77560>>将要显示单元格indexPath为:[0, 1]
将要显示单元格cell为:<UITableViewCell: 0x7fe74eb16080; frame = (0 45; 428 45); text = '1:开关按钮的用法'; autoresize = W; layer = <CALayer: 0x600003d12de0>>将要显示单元格indexPath为:[0, 2]
将要显示单元格cell为:<UITableViewCell: 0x7fe74ea0b1b0; frame = (0 90; 428 45); text = '2:进度条的用法'; autoresize = W; layer = <CALayer: 0x600003d1a700>>将要显示单元格indexPath为:[0, 3]
将要显示单元格cell为:<UITableViewCell: 0x7fe74ea0c670; frame = (0 135; 428 45); text = '3:文本标签的用法'; autoresize = W; layer = <CALayer: 0x600003d1a9e0>>
  • 示例代码:
// 获取选中项的索引
tableView.rx.willDisplayCell.subscribe(onNext: { cell, indexPath inprint("将要显示单元格indexPath为:\(indexPath)")print("将要显示单元格cell为:\(cell)\n")
}).disposed(by: disposeBag)

二、RxDataSources

  • 如果 tableview 需要显示多个 section 或者更加复杂的编辑功能时,可以借助 RxDataSource 这个第三方库来帮我们完成。
  • RxDataSource 的本质就是使用 RxSwift 对 UITableView 和 UICollectionView 的数据源做了一层包装,使用它可以大大减少我们的工作量。

① 安装配置

  • CocoaPods:
pod 'RxDataSources', '~> 3.0'
  • Carthage
github "RxSwiftCommunity/RxDataSources" ~> 3.0
  • 手动安装:
    • 在 RxDataSources 上将 RxDataSources 下载到本地,并引入到项目中来:

  • 在代码中将其 import 进来即可:
import RxDataSources

② 单分区的 TableView

  • 现在需要实现如下效果:

  • RxDataSources 是以 section 来做为数据结构的,所以不管 tableView 是单分区还是多分区,在使用 RxDataSources 的过程中,都需要返回一个 section 的数组。
  • 使用自带的 Section:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 初始化数据
let items = Observable.just([SectionModel(model: "", items: ["UILable的用法","UIText的用法","UIButton的用法"])])// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>(configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "\(indexPath.row):\(element)"return cell})// 绑定单元格数据
items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: DisposeBag())
  • 使用自定义的 Section:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 初始化数据
let sections = Observable.just([MySection(header: "", items: ["UILable的用法","UIText的用法","UIButton的用法"])])// 创建数据源
let dataSource = RxTableViewSectionedAnimatedDataSource<MySection>(// 设置单元格configureCell: { ds, tv, ip, item inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")?? UITableViewCell(style: .default, reuseIdentifier: "Cell")cell.textLabel?.text = "\(ip.row):\(item)"return cell
})// 绑定单元格数据
sections.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: DisposeBag())// 自定义Section
struct MySection {var header: Stringvar items: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity: String {return header}init(original: MySection, items: [Item]) {self = originalself.items = items}
}

③ 多分区的 UITableView

  • 现在要实现如下效果:

  • 使用自带的 Section:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 初始化数据
let items = Observable.just([SectionModel(model: "基本控件", items: ["UILable的用法","UIText的用法","UIButton的用法"]),SectionModel(model: "高级控件", items: ["UITableView的用法","UICollectionViews的用法"])])// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>(configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "\(indexPath.row):\(element)"return cell
})// 设置分区头标题
dataSource.titleForHeaderInSection = { ds, index inreturn ds.sectionModels[index].model
}// 设置分区尾标题
//dataSource.titleForFooterInSection = { ds, index in
//    return "footer"
//}// 绑定单元格数据
items.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
  • 使用自定义的 Section:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 初始化数据
let sections = Observable.just([MySection(header: "基本控件", items: ["UILable的用法","UIText的用法","UIButton的用法"]),MySection(header: "高级控件", items: ["UITableView的用法","UICollectionViews的用法"])])// 创建数据源
let dataSource = RxTableViewSectionedAnimatedDataSource<MySection>(// 设置单元格configureCell: { ds, tv, ip, item inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")?? UITableViewCell(style: .default, reuseIdentifier: "Cell")cell.textLabel?.text = "\(ip.row):\(item)"return cell},// 设置分区头标题titleForHeaderInSection: { ds, index inreturn ds.sectionModels[index].header}// 自定义Section
struct MySection {var header: Stringvar items: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity: String {return header}init(original: MySection, items: [Item]) {self = originalself.items = items}
}

三、刷新表格数据

  • 很多情况下,表格里的数据不是一开始就准备好的、或者固定不变的,可能需要先向服务器请求数据,再将获取到的内容显示在表格中。
  • 要重新加载表格数据,过去的做法就是调用 tableView 的 reloadData() 方法,那么在使用 RxSwift 的情况下,应该如何刷新表格的数据呢?

① 数据刷新

  • 如下所示:
    • 界面初始化完毕后,tableView 默认会加载一些随机数据;
    • 点击右上角的刷新按钮,tableView 会重新加载并显示一批新数据;
    • 为方便演示,每次获取数据不是真的去发起网络请求,而是在本地生成后延迟 2 秒返回,模拟这种异步请求的情况。

  • 示例代码:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self,forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 随机的表格数据
let randomResult = refreshButton.rx.tap.asObservable().startWith(()) // 为了一开始就能自动请求一次数据.flatMapLatest(getRandomResult).share(replay: 1)// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "条目\(indexPath.row):\(element)"return cell})// 绑定单元格数据
randomResult.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)// 获取随机数据
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {print("正在请求数据...")let items = (0 ..< 5).map {_ inInt(arc4random())}let observable = Observable.just([SectionModel(model: "S", items: items)])return observable.delay(2, scheduler: MainScheduler.instance)
}

② 防止表格多次刷新

  • flatMapLatest 的作用是当在短时间内(上一个请求还没回来)连续点击多次“刷新”按钮,虽然仍会发起多次请求,但表格只会接收并显示最后一次请求,避免表格出现连续刷新的现象:
// 随机的表格数据
let randomResult = refreshButton.rx.tap.asObservable().startWith(()) // 为了一开始就能自动请求一次数据.flatMapLatest(getRandomResult).share(replay: 1)
  • 也可以对源头进行限制,即通过 throttle 设置个阀值(比如 1 秒),如果在1秒内有多次点击则只取最后一次,那么自然也就只发送一次数据请求:
// 随机的表格数据
let randomResult = refreshButton.rx.tap.asObservable().throttle(1, scheduler: MainScheduler.instance) // 在主线程中操作,1秒内值若多次改变,取最后一次.startWith(()) // 为了一开始就能自动请求一次数据.flatMapLatest(getRandomResult).share(replay: 1)

③ 停止数据请求

  • 在实际项目中我们可能会需要对一个未完成的网络请求进行中断操作,比如切换页面或者分类时,如果上一次的请求还未完成就要将其取消掉。那么 RxSwift 如何实现该功能呢?
  • 该功能简单说就是通过 takeUntil 操作符实现,当 takeUntil 中的 Observable 发送一个值时,便会结束对应的 Observable:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self,forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 随机的表格数据
let randomResult = refreshButton.rx.tap.asObservable().startWith(()) // 为一开始就能自动请求一次数据.flatMapLatest{self.getRandomResult().takeUntil(self.cancelButton.rx.tap)}.share(replay: 1)// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "条目\(indexPath.row):\(element)"return cell})// 绑定单元格数据
randomResult.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)// 获取随机数据
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {print("正在请求数据......")let items = (0 ..< 5).map {_ inInt(arc4random())}let observable = Observable.just([SectionModel(model: "S", items: items)])return observable.delay(2, scheduler: MainScheduler.instance)
}

四、表格数据的搜索过滤

  • 在 tableView 的表头上增加了一个搜索框,tableView 会根据搜索框里输入的内容实时地筛选并显示出符合条件的数据(包含有输入文字的数据条目)。
  • 这个实时搜索是对已获取到的数据进行过滤,即每次输入文字时不会重新发起请求。


  • 示例代码:
// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self,forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 创建表头的搜索栏
self.searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 56))
self.tableView.tableHeaderView = self.searchBar// 随机的表格数据
let randomResult = refreshButton.rx.tap.asObservable().startWith(()) // 为一开始就能自动请求一次数据.flatMapLatest(getRandomResult) // 获取数据.flatMap(filterResult) // 筛选数据.share(replay: 1)// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "条目\(indexPath.row):\(element)"return cell})// 绑定单元格数据
randomResult.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)// 获取随机数据
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {print("正在请求数据...")let items = (0 ..< 5).map {_ inInt(arc4random())}let observable = Observable.just([SectionModel(model: "S", items: items)])return observable.delay(2, scheduler: MainScheduler.instance)
}// 过滤数据
func filterResult(data:[SectionModel<String, Int>]) -> Observable<[SectionModel<String, Int>]> {return self.searchBar.rx.text.orEmpty//.debounce(0.5, scheduler: MainScheduler.instance) //只有间隔超过0.5秒才发送.flatMapLatest{query -> Observable<[SectionModel<String, Int>]> inprint("正在筛选数据(条件为:\(query))")// 输入条件为空,则直接返回原始数据if query.isEmpty{return Observable.just(data)}// 输入条件为不空,则只返回包含有该文字的数据else{var newData:[SectionModel<String, Int>] = []for sectionModel in data {let items = sectionModel.items.filter{ "\($0)".contains(query) }newData.append(SectionModel(model: sectionModel.model, items: items))}return Observable.just(newData)}}
}

五、可编辑表格

① 实现效果

  • 程序启动后表格会自动加载 5 条随机数据,点击“刷新”按钮则又重新生成 5 条数据并显示;
  • 点击“加号”图标后,会在当前数据集的末尾添加一条随机数据并显示;
  • 点击单元格左侧的“减号”图标则可以将该行数据删除;
  • 拖动单元格右侧的控制图标可以改变显示顺序。


② 示例代码

  • 由于编辑操作比较多,使用 enum 创建一个命令枚举,里面定义了对 tableView 数据的各种操作:
// 定义各种操作命令
enum TableEditingCommand {case setItems(items: [String])              // 设置表格数据case addItem(item: String)                 // 新增数据case moveItem(from: IndexPath, to: IndexPath) // 移动数据case deleteItem(IndexPath)                      // 删除数据
}
  • 接着定义 tableView 对应的 ViewModel,这里面除了保存有表格数据外,还包含上面定义的 4 个操作命令的具体实现:
// 定义表格对应的ViewModel
struct TableViewModel {// 表格数据项fileprivate var items:[String]init(items: [String] = []) {self.items = items}// 执行相应的命令,并返回最终的结果func execute(command: TableEditingCommand) -> TableViewModel {switch command {case .setItems(let items):print("设置表格数据。")return TableViewModel(items: items)case .addItem(let item):print("新增数据项。")var items = self.itemsitems.append(item)return TableViewModel(items: items)case .moveItem(let from, let to):print("移动数据项。")var items = self.itemsitems.insert(items.remove(at: from.row), at: to.row)return TableViewModel(items: items)case .deleteItem(let indexPath):print("删除数据项。")var items = self.itemsitems.remove(at: indexPath.row)return TableViewModel(items: items)}}
}
  • 主视图控制器代码:
class ViewController: UIViewController {// 刷新按钮@IBOutlet weak var refreshButton: UIBarButtonItem!// 新增按钮@IBOutlet weak var addButton: UIBarButtonItem!// 表格var tableView:UITableView!let disposeBag = DisposeBag()override func viewDidLoad() {super.viewDidLoad()// 创建表格视图self.tableView = UITableView(frame: self.view.frame, style:.plain)// 创建一个重用的单元格self.tableView!.register(UITableViewCell.self,forCellReuseIdentifier: "Cell")self.view.addSubview(self.tableView!)// 表格模型let initialVM = TableViewModel()// 刷新数据命令let refreshCommand = refreshButton.rx.tap.asObservable().startWith(()).flatMapLatest(getRandomResult).map(TableEditingCommand.setItems)// 新增条目命令let addCommand = addButton.rx.tap.asObservable().map{ "\(arc4random())" }.map(TableEditingCommand.addItem)// 移动位置命令let movedCommand = tableView.rx.itemMoved.map(TableEditingCommand.moveItem)// 删除条目命令let deleteCommand = tableView.rx.itemDeleted.asObservable().map(TableEditingCommand.deleteItem)// 绑定单元格数据Observable.of(refreshCommand, addCommand, movedCommand, deleteCommand).merge().scan(initialVM) { (vm: TableViewModel, command: TableEditingCommand)-> TableViewModel inreturn vm.execute(command: command)}.startWith(initialVM).map {[AnimatableSectionModel(model: "", items: $0.items)]}.share(replay: 1).bind(to: tableView.rx.items(dataSource: ViewController.dataSource())).disposed(by: disposeBag)}override func viewDidAppear(_ animated: Bool) {super.viewDidAppear(animated)tableView.setEditing(true, animated: true)}// 获取随机数据func getRandomResult() -> Observable<[String]> {print("生成随机数据。")let items = (0 ..< 5).map {_ in"\(arc4random())"}return Observable.just(items)}
}extension ViewController {// 创建表格数据源static func dataSource() -> RxTableViewSectionedAnimatedDataSource<AnimatableSectionModel<String, String>> {return RxTableViewSectionedAnimatedDataSource(// 设置插入、删除、移动单元格的动画效果animationConfiguration: AnimationConfiguration(insertAnimation: .top,reloadAnimation: .fade,deleteAnimation: .left),configureCell: {(dataSource, tv, indexPath, element) inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")!cell.textLabel?.text = "条目\(indexPath.row):\(element)"return cell},canEditRowAtIndexPath: { _, _ inreturn true // 单元格可删除},canMoveRowAtIndexPath: { _, _ inreturn true // 单元格可移动})}
}

六、不同类型的单元格混用

① 效果展示

  • tableView 绑定的数据源中一共有 2 个 section,每个 section 里分别有 3 条数据需要显示。
  • 每个 cell 会根据数据类型的不同,自动选择相应的显示方式:“文字+图片”或“文字+开关按钮”。

② 示例代码

// 初始化数据
let sections = Observable.just([MySection(header: "我是第一个分区", items: [.TitleImageSectionItem(title: "图片数据1", image: UIImage(named: "php")!),.TitleImageSectionItem(title: "图片数据2", image: UIImage(named: "react")!),.TitleSwitchSectionItem(title: "开关数据1", enabled: true)]),MySection(header: "我是第二个分区", items: [.TitleSwitchSectionItem(title: "开关数据2", enabled: false),.TitleSwitchSectionItem(title: "开关数据3", enabled: false),.TitleImageSectionItem(title: "图片数据3", image: UIImage(named: "swift")!)])])// 创建数据源
let dataSource = RxTableViewSectionedReloadDataSource<MySection>(// 设置单元格configureCell: { dataSource, tableView, indexPath, item inswitch dataSource[indexPath] {case let .TitleImageSectionItem(title, image):let cell = tableView.dequeueReusableCell(withIdentifier: "titleImageCell",for: indexPath)(cell.viewWithTag(1) as! UILabel).text = title(cell.viewWithTag(2) as! UIImageView).image = imagereturn cellcase let .TitleSwitchSectionItem(title, enabled):let cell = tableView.dequeueReusableCell(withIdentifier: "titleSwitchCell",for: indexPath)(cell.viewWithTag(1) as! UILabel).text = title(cell.viewWithTag(2) as! UISwitch).isOn = enabledreturn cell}},// 设置分区头标题titleForHeaderInSection: { ds, index inreturn ds.sectionModels[index].header}
)// 绑定单元格数据
sections.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)// 单元格类型
enum SectionItem {case TitleImageSectionItem(title: String, image: UIImage)case TitleSwitchSectionItem(title: String, enabled: Bool)
}// 自定义Section
struct MySection {var header: Stringvar items: [SectionItem]
}extension MySection : SectionModelType {typealias Item = SectionIteminit(original: MySection, items: [Item]) {self = originalself.items = items}
}

七、UITableView 相关样式的修改

① 修改单元格高度

  • 示例代码:
var tableView:UITableView!
var dataSource:RxTableViewSectionedAnimatedDataSource<MySection>?
let disposeBag = DisposeBag()// 创建表格视图
self.tableView = UITableView(frame: self.view.frame, style:.plain)
// 创建一个重用的单元格
self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(self.tableView!)// 初始化数据
let sections = Observable.just([MySection(header: "基本控件", items: ["UILable的用法","UIText的用法","UIButton的用法"]),MySection(header: "高级控件", items: ["UITableView的用法","UICollectionViews的用法"])])// 创建数据源
let dataSource = RxTableViewSectionedAnimatedDataSource<MySection>(// 设置单元格configureCell: { ds, tv, ip, item inlet cell = tv.dequeueReusableCell(withIdentifier: "Cell")?? UITableViewCell(style: .default, reuseIdentifier: "Cell")cell.textLabel?.text = "\(ip.row):\(item)"return cell},// 设置分区头标题titleForHeaderInSection: { ds, index inreturn ds.sectionModels[index].header}
)self.dataSource = dataSource// 绑定单元格数据
sections.bind(to: tableView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)// 设置代理
tableView.rx.setDelegate(self).disposed(by: disposeBag)// tableView代理实现
extension MainViewController : UITableViewDelegate {// 设置单元格高度func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)-> CGFloat {guard let _ = dataSource?[indexPath],let _ = dataSource?[indexPath.section]else {return 0.0}return 60}
}// 自定义Section
struct MySection {var header: Stringvar items: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity: String {return header}init(original: MySection, items: [Item]) {self = originalself.items = items}
}

② 修改分组的头部和尾部

  • 修改代理实现,示例代码:
// tableView代理实现
extension MainViewController : UITableViewDelegate {// 返回分区头部视图func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int)-> UIView? {let headerView = UIView()headerView.backgroundColor = UIColor.blacklet titleLabel = UILabel()titleLabel.text = self.dataSource?[section].headertitleLabel.textColor = UIColor.whitetitleLabel.sizeToFit()titleLabel.center = CGPoint(x: self.view.frame.width/2, y: 20)headerView.addSubview(titleLabel)return headerView}// 返回分区头部高度func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int)-> CGFloat {return 40}
}// 自定义Section
struct MySection {var header: Stringvar items: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity: String {return header}init(original: MySection, items: [Item]) {self = originalself.items = items}
}

RxSwift之UI控件UITableView扩展的使用相关推荐

  1. RxSwift之UI控件UIGestureRecognizer扩展的使用

    RxCocoa 同样对 UIGestureRecognizer 进行了扩展,并增加相关的响应方法.现在以滑动手势为例做具体说明,其它手势用法也是一样的. 当手指在界面上向上滑动时,弹出提示框,并显示出 ...

  2. RxSwift之UI控件UICollectionView扩展的使用

    一.基本用法 ① 单个分区的集合视图 效果如下: 示例代码: // 定义布局方式以及单元格大小 let flowLayout = UICollectionViewFlowLayout() flowLa ...

  3. RxSwift之UI控件UIPickerView扩展的使用

    一.基本用法 ① 单列的情况 基本用法 示例代码: var pickerView : UIPickerView!// 最简单的pickerView适配器(显示普通文本) private let str ...

  4. RxSwift之UI控件Label扩展的使用

    一.将数据绑定到 text 属性上(普通文本) 当程序启动时就开始计时,同时将已过去的时间格式化后显示在 label 标签上: // 创建文本标签 let label = UILabel(frame: ...

  5. RxSwift之UI控件UITextField与UITextView扩展的使用

    一.监听单个 textField 内容的变化(textView 同理) 将 textField 里输入的内容实时地显示到控制台中,示例代码: // 创建文本输入框 let textField = UI ...

  6. RxSwift之UI控件UISwitch与UISegmentedControl扩展的使用

    一.UISwitch(开关按钮) 实现当 switch 开关状态改变时,输出当前值: 示例代码: switch1.rx.isOn.asObservable().subscribe(onNext: {p ...

  7. RxSwift之UI控件UISlider与UIStepper扩展的使用

    一.UISlider(滑块) 如下所示,当拖动滑块时,在控制台中实时输出 slider 当前值: 当前值为:0.308442 当前值为:0.349990 当前值为:0.400087 当前值为:0.45 ...

  8. RxSwift之UI控件UIActivityIndicatorView与UIApplication扩展的使用

    一.UIActivityIndicatorView(活动指示器) UIActivityIndicatorView 又叫状态指示器,它会通过一个旋转的"菊花"来表示当前的活动状态. ...

  9. RxSwift之UI控件UIButton与UIBarButtonItem扩展的使用

    一.点击事件响应 如果想实现点击按钮后,弹出一个消息提示框: 示例代码: import UIKit import RxSwift import RxCocoaclass ViewController: ...

最新文章

  1. 装饰模式(Decorator Pattern)--------结构型模式
  2. Egit Patch
  3. Qt小游戏《2048》源码(含大量注释)
  4. 反馈网络信息改善用户体验
  5. 在apache中设置访问目录后进入的默认页面为index.php
  6. 辉哥给rockchip修复了一个内存溢出问题
  7. 浪潮科大讯飞Altera用OpenCL实现FPGA深度学习语音识别加速方案
  8. lol2月26日更新后一直提示服务器维护,《lol》2月26日更新了什么 2月26日更新内容一览...
  9. Oracle迁移PPAS:中文表名的处理
  10. 20191129每日一句
  11. f018计算机辅助设计是什么,F018,丝印Marking-电子元件丝印查询
  12. WiFi技术简述与发展
  13. Sketch的下载与安装
  14. 浅谈文字编码和Unicode(上)
  15. 【C++】优先级队列priority_queue模拟实现仿函数
  16. Ubuntu上搭建Hadoop环境(单机模式+伪分布模式) - 狂奔的蜗牛 - 博客频道 - CSDN.NET http://blog.csdn.net/hitwengqi/article/detai
  17. UTC相关的时区转换
  18. Linux下的搜狗拼音输入法-fcitx sougopinyin
  19. [机缘参悟-33]:眼见不一定为实,大多数时候“眼见为虚”
  20. 扇贝python_扇贝编程(python)手机版-扇贝编程app下载v1.1.30-汉化新世纪

热门文章

  1. SP2-0618: Cannot find the Session Identifier. Check PLUSTRACE role is enable
  2. 程序员奇奇怪怪的网络问题
  3. 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
  4. angularJs-脏检查
  5. web开发中的长度单位(px,em,ex,rem),如何运用,看完这篇就够了!
  6. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析
  7. HTML转PDF(C#---itextsharp)(转自别人的文章)
  8. 虚拟成像技术_苹果AR头显细节全曝光!微美全息(WIMI.US)光场技术构建AI影像...
  9. 7-4 jmu-Java-06异常-04-自定义异常(综合) (15 分)
  10. oracle / parallle /,Oracle海量数据迁移之使用shell启用多个动态并行