前言
本文介绍了UICollectionView的用法的扩展,不涉及基础的用法,是我对UICollectionView用法的总结和实战,通过这篇文章,读者可以了解到使用自定义的UICollectionViewDataSouce
来和一些小技巧,来使VC更加轻量,在下篇文章中,我会继续介绍UICollectionViewLayout
的扩展,并且使用UICollectionViewLayout
做出一些酷炫的动画,如文章中有错误,欢迎大家指出。
文章参考于:Objc.IO,王巍的博客
UICollectionView简介
- UICollectionView可以说是和UITableView的扩展,去掉UICollectionViewLayout可以说和UITableView没有太多区别,但当使用Layout时,CollectionView就展示出了自己的高灵活性,可以实现一下非常复杂的布局。
基本的UICollectionView设定
让我们先做点热身,设置一些基本的设定
- 初始化一个
UICollectionView
,一定要在初始化时就将layout
属性定义在初始化中,不然程序会崩溃,然后让其Conformdelegate
和dataSource
,并注册一个Cell。 - 这一篇不介绍
UICollectionViewLayout
的自定义,所以先使用系统提供的UICollectionViewFlowLayout
来进行初始化,UICollectionViewFlowLayout是系统提供的流式布局,Cell按顺序排开,一行里的Cell有位置则会在同一行,没有位置的Cell会排在下一行,UICollectionViewFlowLayout的设定可以通过属性,也可以符合UICollectionViewDelegateFlowLayout
来设定,详情可见文档。
private var layout = UICollectionViewFlowLayout()
private lazy var collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(LNCollectionViewCell.self, forCellWithReuseIdentifier: "LNCollectionViewCell")
collectionView.delegate = self
collectionView.dataSource = self
self.view.addSubview(collectionView)
}
UICollectionViewDataSource
- UICollectionViewDataSouce的基本设定如下,返回一个Cell的数量,并设定Cell的样式和使Cell能够复用,到现在为止基本设定已经完成。
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LNCollectionViewCell", for: indexPath) as! LNCollectionViewCell
let item = self.items[indexPath.item]
cell.titleLabel.text = item.text
return cell
}
更轻量的ViewController
UICollectionViewDataSource往往在Controller里拥有着大量篇幅,多到几十行,甚至上百行,那如何在这方面使Controller更加轻量呢,也许你会说使用extension
,使用extension是一个不错的办法,可以使Controller更加清晰,但并没有做到轻量,因为代码还是在这里,Controller依然不堪重负
- 自定义DataSource并将业务逻辑转移至DataSorce的类里,定义一个
Block
,使用Block为来设置Cell
var ConfigureCellBlock: ((_ cell: LNCollectionViewCell, _ indexPath: IndexPath, _ item: LNToDoItem) -> Void)?
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LNCollectionViewCell", for: indexPath) as! LNCollectionViewCell
let item = self.items[indexPath.item]
if let setUpCellBlock = ConfigureCellBlock {
setUpCellBlock(cell, indexPath, item)
}
return cell
}
现在可以把ViewController中的代码取掉了
- 简单的使用几行,就可以了
private var dataSource = LNCollectionViewDataSource()
collectionView.dataSource = dataSource
dataSource.ConfigureCellBlock = { cell, indexPath, item in
let text = item.title
cell.titleLabel.text = text
}
随着业务逻辑增加,DataSource的类减轻的负担就越明显
建立起Cell和Model之间的桥梁
Cell属于View层,这里不是说要让View和Model直接通行,而是在DataSource中,直接设定Cell是错误的,也是麻烦的,因为DataSource不应该知道Cell的实现细节,所以应该将Cell的实现逻辑内联到自己的实现区域里,这里使用Extension,在Cell内部进行设置
Extension Cell {
func configureCell(with todo: LNTodoItem) {
cell.label.text = todo.name
cell.dateLabel.text = todo.configurePresentationDate()
}
}
在此,使用Model来将Cell进行设定,使用Model的name属性为Label赋值,使用方法来将预定好的Date显示出来,因为处理日期显示是很麻烦的一件事,这里让Model内部处理日期,会节省很多代码
让Model做自己该做的事
- 为Model定义储存类,使Model做自己的事,而使代码从ViewController中分离出来,ViewController只负责处理协调Model和View,MVC之间应该使用单向流程,当Model发生变化,应该间接向Controller汇报。
class LNStore {
static var shared = LNStore()
private(set) var items = [LNToDoItem]()
var count: Int { items.count }
var indices: Range<Int> { items.indices }
init() { }
func append(item: LNToDoItem) {
items.append(item)
}
func remove(item: LNToDoItem) {
guard let index = items.firstIndex(of: item) else { return }
items.remove(at: index)
}
func remove(at index: Int) {
items.remove(at: index)
}
func edit(origin: LNToDoItem, new: LNToDoItem) {
guard let index = items.firstIndex(of: origin) else { return }
items[index] = new
}
}
将View转移到View层
- View的定义转移到自己的类里,而不是在viewDidLoad()中设置,该方法只用来设定一些View在自己的类中无法单独实现的设置