swift ~ MVVM之UITableView绑定
2018-06-14 本文已影响193人
RudyHao
本篇讲解一行代码搞定UITableView绑定
- 原来我们的UITableView可能是这样的
final class ViewController: UIViewController{
//数据源
var data = []
/**
* 创建tableView
*/
lazy var tableView: UITableView! = {
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.register(nibWithCellClass: TableViewCell.self)
self.view.addSubview(tableView)
return tableView
}()
}
extension ViewController: UITableViewDelegate {
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 30
}
}
extension ViewController: UITableViewDataSource {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withClass: SVW_PowerListTableViewCell.self)
if indexPath.row >= data.count {
fatalError("index over items court")
}
guard let item = data.item(at: indexPath.row) else {
fatalError("cell model is empty")
}
do {
try cell?.setLayoutModel(item)
} catch {
fatalError(error.localizedDescription)
}
return cell!
}
}
对,我们大多的UITableView都是长这样子,而且一个app可能有很多这样的代码;有了Rx就可以一行代码搞定啦...请看码:
- 改造后的代码是这样的:
final class ViewController: UIViewController{
var tbAdapter:TableViewBindingAdapter<Model>?
var viewModel = ViewModel()
/**
* 创建tableView
*/
lazy var tableView: UITableView! = {
let tableView = UITableView()
//⚠️⚠️⚠️请注意这三行不需要了
//tableView.delegate = self
//tableView.dataSource = self
//tableView.register(nibWithCellClass: TableViewCell.self)
self.view.addSubview(tableView)
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
//别看这么长,实际就一行😄
//同时需要TableViewCell实现ReactiveView协议《可读源码&改造源码》
self.tbAdapter = TableViewBindingAdapter<Model>(
tableView: self.tableView,
sourceSignal: (viewModel.dataSource),
nibName: TableViewCell.getClassName())
}
}
怎么样,是不是精神气爽?哈哈,感谢Rx给开发带来的便捷...
基本思路
:Android的ListView适配器模式
资料来源
:链接
思路拓展
:可能有同学会问我想在tableview添加更复杂的功能咋办?so easy,重写一个适合自己需要的adapter即可
TableViewBindingAdapter源代码
//
// TableViewBindingAdapter.swift
// ReactiveSwiftFlickrSearch
//
// Created by Colin Eberhardt on 15/07/2014.
// Copyright (c) 2014 Colin Eberhardt. All rights reserved.
//
import Foundation
import UIKit
import RxSwift
import RxCocoa
protocol ReactiveView {
func getCellHeight<M>(model: M)->CGFloat
func bindViewModel<M>(model: M)
}
// a helper that makes it easier to bind to UITableView instances
// see: http://www.scottlogic.com/blog/2014/05/11/reactivecocoa-tableview-binding.html
class TableViewBindingAdapter<T>: NSObject, UITableViewDataSource, UITableViewDelegate {
//MARK: Properties
private var disposeBag = DisposeBag()
var delegate: UITableViewDelegate?
private let tableView: UITableView
private let templateCell: UITableViewCell
private let rowCommand: PublishSubject<AnyObject>?
private let cellCommand: PublishSubject<AnyObject>?
private let sectionView: UIView?
private var data: [T]
/// table adapter
///
/// - Parameters:
/// - tableView: tableview
/// - sourceSignal: 数据源
/// - nibName: tableviewcell xib
/// - rowCommand: 行点击时间
/// - cellCommand: 行内按钮点击事件
/// - sectionView: table section
init(tableView: UITableView, sourceSignal: Observable<[T]?>, nibName: String, rowCommand: PublishSubject<AnyObject>? = nil,cellCommand: PublishSubject<AnyObject>? = nil, sectionView:UIView? = nil) {
self.tableView = tableView
self.data = []
self.rowCommand = rowCommand
self.cellCommand = cellCommand
self.sectionView = sectionView
let nib = UINib(nibName: nibName, bundle: nil)
// create an instance of the template cell and register with the table view
templateCell = nib.instantiate(withOwner: nil, options: nil)[0] as! UITableViewCell
tableView.register(nib, forCellReuseIdentifier: NSStringFromClass(templateCell.classForCoder))
super.init()
sourceSignal.subscribe(onNext: {[weak self] (dic) in
if dic != nil{
self?.data = dic!
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: {
self?.tableView.reloadData()
})
}
}).disposed(by: self.disposeBag)
tableView.dataSource = self
tableView.delegate = self
}
//MARK: Private
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (self.sectionView != nil) {
return self.sectionView!.height
}else{
return 0
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if (self.sectionView != nil) {
return self.sectionView
}else{
return nil
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item: T = data[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(templateCell.classForCoder))! as UITableViewCell
cell.event = self.cellCommand
if let reactiveView = cell as? ReactiveView {
reactiveView.bindViewModel(model: item)
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(templateCell.classForCoder))! as UITableViewCell
let item: T = data[indexPath.row]
if let reactiveView = cell as? ReactiveView {
return reactiveView.getCellHeight(model: item)
}
else{
return templateCell.frame.size.height
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if rowCommand != nil {
rowCommand?.onNext(data[indexPath.row] as AnyObject)
}
tableView.deselectRow(at: indexPath, animated: true)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.delegate?.responds(to: #selector(UIScrollViewDelegate.scrollViewDidScroll(_:))) == true {
self.delegate?.scrollViewDidScroll?(scrollView);
}
}
deinit{
print("******************" + String(describing: self)+"******************deinit")
}
}
持续分享源码待续...