文章目录
- RxSwift - 实现一个MVVM架构的TableView
- 前沿
- MVVM架构的Tableview
- 目录结构
- 1、模型(Model)
- 2、视图模型(ViewModel)
- 3、视图(View)
- 界面效果
RxSwift - 实现一个MVVM架构的TableView
前沿
MVVM
架构在在实际开发中被广泛应用,它让代码结构清晰美观,易于阅读维护,同时也弥补了MVC
结构中Controller
臃肿的问题
今天来实现一个基于RxSwift
的基础TableView
页面
效果:使用
RxSwift
,将View
与Model
进行绑定,实现动态修改数据时更新UI
MVVM架构的Tableview
目录结构
以下是目录结构
目录由ViewModel
、View
、Model
三个文件夹组成
1、模型(Model)
在Model
文件夹下新建Product
文件
import Foundation
struct Product {
let imgName: String // 图
let name: String// 名称
let price: String// 价格
}
模型简单表示了一个商品的
2、视图模型(ViewModel)
在ViewModel
文件夹下新建ProductViewModel
文件。它相当于是View
和Model
的桥梁,在ViewModel
中会有相应的获取数据以及处理数据的方法,然后将数据传输到View
import Foundation
import RxSwift
class ProductViewModel {
// PublishSubject: 只会发送新的事件给订阅者,订阅之前的事件不会发送
// BehaviorSubject: 会保持最新的值,并将其发送给新的订阅者
let items = PublishSubject<[Product]>()
// let items = BehaviorSubject<[Product]>(value: [])
var productArray: [Product]!
func fetchProductList() {
// 在这里可以做网络请求
// 咱们就直接用假数据
productArray = [
Product(imgName: "apple", name: "apple", price: "10"),
Product(imgName: "banana", name: "banana", price: "5"),
Product(imgName: "pear", name: "pear", price: "4"),
Product(imgName: "watermelon", name: "watermelon", price: "3"),
Product(imgName: "mango", name: "mango", price: "8")
]
items.onNext(productArray)
// items.onCompleted()
}
func addData() {
productArray.append(Product(imgName: "peach", name: "peach", price: "7"))
items.onNext(productArray)
}
}
该类中:
- 定义了时间发布者
items
,使用PublishSubject
类型 - 定义获取数据的方法
fetchProductList()
,获取完数据后,使用onNext
将事件发布出去。代码中注释掉onCompleted()
,是因为想要实现后续数据更新的操作,onCompleted
会终止序列,使其不再接收新的元素。 - 定义
addData()
方法,界面中将通过点击按钮
模拟增加数据操作
3、视图(View)
在View
层,首先有个简单的CellProductTableViewCell
,它有一个数据有属性item
,赋值时进行UI内容样式设置
import UIKit
class ProductTableViewCell: UITableViewCell {
var item: Product? = nil {
didSet{
textLabel?.text = item?.name
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
接下来是ViewController
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController, UIScrollViewDelegate {
private let bag = DisposeBag()
private let viewModel = ProductViewModel()
lazy var tableView: UITableView = {
let tableView = UITableView(frame: view.bounds, style: UITableView.Style.grouped)
view.addSubview(tableView)
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.rx.setDelegate(self).disposed(by: bag)
bindTableView()
let btn:UIButton = UIButton(type: .system)
btn.frame = CGRectMake(10, view.frame.size.height - 80, view.frame.size.width - 20, 50)
btn.backgroundColor = .lightGray
btn.setTitle("addData", for: UIControl.State.normal)
btn.rx.tap
.subscribe(onNext: { [unowned self] in
self.viewModel.addData()
}).disposed(by: bag)
view.addSubview(btn)
}
private func bindTableView() {
tableView.register(ProductTableViewCell.self, forCellReuseIdentifier: "cellId")
viewModel.items.bind(to: tableView.rx.items(cellIdentifier: "cellId", cellType: ProductTableViewCell.self)) { (row,item,cell) in
cell.item = item
}.disposed(by: bag)
tableView.rx.modelSelected(Product.self).subscribe(onNext: { item in
print("SelectedItem: \(item.name)")
}).disposed(by: bag)
viewModel.fetchProductList()
}
}
- 定义
private let bag = DisposeBag()
,作用就是在合适的时机自动调用这些 Disposable 对象的 dispose() 方法,释放资源,避免内存泄漏 - 持有
ViewModel
:private let viewModel = ProductViewModel()
- 初始化
tableView
viewDidLoad()
中,指定了tableView
代理为self
,然后将viewModel
的items
事件绑定到tableView
,即将数据源
绑定到表视图行
。同时订阅了选中某个模型的事件modelSelected
,即选中某个Cell的事件。(使用RxCocoa
提供的方法实现)- 增加一个按钮,点击时调用
viewModel.addData()
方法,动态修改数据源。因为已将tableView
绑定到数据源,视图也将动态刷新
界面效果
@oubijiexi