持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
前言
又来撸RXSwift了
如果您还没有阅读上一部分,它可以在这里找到。在上一篇中,我们实现了如何设置只有一种cell的 collectionview。
这一part,咱们来用RXSwift写一个多section的Collectionview
还是要引入
pod RxDataSources
这个库可以帮我们实现更多功能,使用它可以大大减少我们的工作量。更多功能可以自行百度。
正文
项目的目录结构
第一步 定义Model
咱们在Model文件夹中创建 Product.swift模型
struct Product {
let name: String// 名称
let price: String// 价格
}
第二步 视图模型(viewModel)
在ViewModel文件夹下新建ProductViewModel.swift 然后导入
import RxSwift
import RxDataSources
然后定义一个section 使用enum
enum CollectionSection{
case main(String)
}
这个 相关值类型的枚举,咱们定义为string类型
然后定义Item
enum CollectionItem {
case Prod(info: Product)
}
同样这个Item的相关类型枚举,设置为 Product模型
然后咱们来定义 SectionModel
typealias SectionOfProduct = SectionModel<CollectionSection,CollectionItem>
这里的SectionModel是来自RxDataSources,它有两个参数,一个是Section,另一个是Item,咱们就用上面定义好的CollectionSection 和 CollectionItem传入
所以,SectionOfProduct 就相当于一个section里的所有内容了,初始化它也十分简单
SectionOfProduct(model: .main(xxx), items: [])
最后,咱们做模拟数据
class ProductViewModel {
let items = PublishSubject<[SectionOfProduct]>()
func getInformation() {
var subItems_iphone = [CollectionItem]()
subItems_iphone = ([
.Prod(info: Product(name: "iPhone11", price: "100")),
.Prod(info: Product(name: "iPhone12", price: "100")),
.Prod(info: Product(name: "iPhone13", price: "100")),
])
var subItems_ipad = [CollectionItem]()
subItems_ipad = ([
.Prod(info: Product(name: "ipad5", price: "100")),
.Prod(info: Product(name: "ipad6", price: "100")),
])
items.onNext([SectionOfProduct(model: .main("iphone"), items: subItems_iphone),
SectionOfProduct(model: .main("ipad"), items: subItems_ipad)
])
}
}
在这个Viewmodel里,我模拟设置了两个section 一个是iphone,另一个是ipad,同时,每个section中又有一组数据。这样,就完成了数据的模拟。
第三步 视图(View)
咱们在View文件夹下创建一个ProductVC
同样的设置 bag 和 viewModel 和一些基本参数
var collectionView: UICollectionView!
var disposeBag = DisposeBag()//初始化清除包
private let viewModel = ProductViewModel()
let SCREEN_WIDTH = UIScreen.main.bounds.size.width
let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
然后懒加载一个 dataSource
private lazy var dataSource = RxCollectionViewSectionedReloadDataSource<SectionOfProduct>(configureCell: {(ds,collectionView,indexPath,ele) -> UICollectionViewCell in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell",for: indexPath) as! MyCollectionViewCell
switch ele {
case .Prod(let info):
cell.viewModel = info
break
}
return cell
},configureSupplementaryView: {(ds,cv,kind,indexPath) in
let section = cv.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "Section", for: indexPath) as! MySectionHeader
let viewModel = ds[indexPath.section]
let model = viewModel.model
switch model {
case .main(let str):
section.headerLB.text = "\(str)"
break
}
return section
})
这里的 dataSource 加载和 tableview有些许不同。
dataSource 数据源初始化的时候,可以将cell和headerview或foodview同时 Reuse,所以,在这里就节省了不少代码。
但是关键不要写错,RXSwift就是这点特别不好,代码提示做的真烂!
最后就是UI设置和bind设置了
func creatUI(){
// 定义布局方式以及单元格大小
let flowLayout = UICollectionViewFlowLayout()
flowLayout.sectionInset = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
flowLayout.headerReferenceSize = CGSize(width: self.view.frame.width, height: 40)
flowLayout.itemSize = CGSize(width: (SCREEN_WIDTH-50)/3.0, height: 180)
// 创建集合视图
self.collectionView = UICollectionView(frame: self.view.frame,
collectionViewLayout: flowLayout)
self.collectionView.backgroundColor = UIColor.white
// 创建一个重用的单元格
self.collectionView.register(UINib.init(nibName: "MyCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
// 创建一个重用的分区头
self.collectionView.register(UINib.init(nibName: "MySectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Section")
self.view.addSubview(self.collectionView!)
}
设置UI时,出于方便,我都用的xib,如果用代码的话,注册cell这里要变一下 注意!
func bindView(){
viewModel.items.bind(to: collectionView.rx.items(dataSource: dataSource)).disposed(by: disposeBag)
viewModel.getInformation()
}
bind比较简单,直接将viewModel.items 绑定到 collectionView 的dataSource上
最后进行 getInformation 模拟请求
就基本上完成了
可以看看效果图
结语
总体来说 在collectionview上使用 RxDataSources 进行多section的设置,代码量少了,但是代码RXSwift上的代码提示也少了,这对初学者十分不友好。希望能会改进把。