前言
本文介紹了UICollectionView的用法的擴展,不涉及基礎的用法,是我對UICollectionView用法的總結和實戰,通過這篇文章,讀者可以瞭解到使用自定義的UICollectionViewDataSouce
來使UIViewController
更加輕量,使用Model
來使UIViewController
更加輕量,在下篇文章中,我會繼續介紹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的類減輕的負擔就越明顯
讓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在自己的類中無法單獨實現的設置