使用RXSwift撸一个CollectionView

912 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情


前言

又来撸RXSwift了

今天来操作一下,使用RXSwift来撸一个collectionview

如果还没看过之前的文章,可以跳转到这里:使用RXSwift撸一个MVVM架构的Tableview

今天这个collectionview,还是先从最基本的一个section开始。下一篇咱们在来点复杂的。

还是先声明 咱们使用的版本是

pod 'RxSwift', '~> 5.0'
pod 'RxCocoa', '~> 5.0'

版本比较新,对应以前的版本,RXSwift有语法上的变化,注意

正文

咱们使用的是MVVM方式构建代码 先看目录

截屏2022-05-27 上午9.48.14.png

第一步: 模型(Model)

我们在Model文件夹下创建一个Product模型

import Foundation

struct Product {
    let name: String// 名称
    let price: String// 价格
}

第二步: 视图模型(ViewModel)

咱们在 ViewModel 文件夹下创建一个 GoodsViewModel 它的作用就是是 View 和 Model 之间的连接器,它可以将获取到的数据传输到 ViewController。

class GoodsViewModel {
    
    let items = PublishSubject<[Goods]>()
    
    func fetchGoodsList() {
            // 在这里可以做网络请求
            // 咱们就直接用假数据
            let goodsArray = [
                Goods( name: "MacBook", price: "100"),
                Goods( name: "iPhone Xs", price: "100"),
                Goods( name: "iWatch", price: "100"),
                Goods( name: "iPad", price: "100"),
                Goods( name: "iPhone X", price: "100")
            ]
        
        items.onNext(goodsArray)
        items.onCompleted()
    }
    
}

第三步 视图(view)

咱们在view文件夹下创建GoodsViewController 继承自 UIViewController

GoodsViewController中 咱们来编写RXSwift的核心代码。

首先呢,要定义两个属性

private let bag = DisposeBag()
private let viewModel = GoodsViewModel()

bag: 是和生命周期相关

viewModel: 就是视图模型拉

然后在viewDidLoad设置collectionview

override func viewDidLoad() {
        super.viewDidLoad()
       // 定义布局方式以及单元格大小
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.sectionInset = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
        flowLayout.itemSize = CGSize(width: (SCREEN_WIDTH-50)/3.0, height: 70)
 
        // 创建集合视图
        self.collectionView = UICollectionView(frame: self.view.frame,
                                               collectionViewLayout: flowLayout)
        self.collectionView.backgroundColor = UIColor.white
 
       
        self.view.addSubview(self.collectionView!)

        bindView()
    }

这里 collectionview的初始化和原来的基本上没什么差别, 咱们来看 bindView()里是如何实现的。

private func bindView() {
       
        // 创建一个重用的单元格
        collectionView.register(MyCollectionViewCell.self,
                                     forCellWithReuseIdentifier: "Cell")
        
        // 绑定cell
        viewModel.items.bind(to: collectionView.rx.items(cellIdentifier: "cellId", cellType: MyCollectionViewCell.self)) { (row,item,cell) in
            cell.label.text = "\(row):\(element)"
        }.disposed(by: bag)
        
        // 获取indexpath
       collectionView.rx.itemSelected.subscribe(onNext: { indexPath in print("选中项的indexPath为:\(indexPath)") }).disposed(by: disposeBag)
       
       // 获取选中项的内容
        collectionView.rx.modelSelected(String.self).subscribe(onNext: { item in
            print("选中项的标题为:\(item)")
        }).disposed(by: disposeBag)
        
        // 模拟请求
        viewModel.fetchProductList()
    }

第一步:先注册cell 。

第二步:将 viewModel.items 绑定到 collectionview 的 cell上,在RXSwift给的闭包中,进行 cell 的赋值操作,这样就省去了设置Datasource。

第三步:在RXSwift中 获取indexpathitem是分别两个函数 itemSelectedh和modelSelected 不像原生中,一个函数就可以完成(这也是一个比较麻烦的事)。但是RXSwift还有一个zip的方法。可以将两个合并(是不是很神奇),咱们来看看

Observable
    .zip(collectionView.rx.itemDeselected, collectionView.rx.modelDeselected(String.self))
    .bind { [weak self] indexPath, item in
       	print("被取消选中项的indexPath为:\(indexPath)")
		print("被取消选中项的的标题为:\(item)")
    }
    .disposed(by: disposeBag)

这样,就实现原来的didselect的方法了。

第四步:还是假装请求。

到这里,一个使用MVVM架构的collectionview就完成了(cell按需可以自己定义,这里就不写了)。其实写法和tableview差不多,手熟尔。

结语

总体来说,使用RXSwift来做collectionview也是比较迅速的。适用于快速布局,但是在大多时候,可能没有机会去用到,这就需要大家可以再重构或者一开始的时候就去考虑使用。这样才有使用记忆。