前言
App里很多頁(yè)面都是用UICollectionView和UITableView做的,比如一些列表類(lèi)的飞盆、詳情類(lèi)的娄琉,甚至是商城首頁(yè)等等次乓,只要是分塊的,特別是根據(jù)數(shù)據(jù)源不同而展示不同的頁(yè)面孽水,都很適合用檬输,但怎么用,使它容易修改匈棘、擴(kuò)展丧慈,是個(gè)問(wèn)題。
產(chǎn)品經(jīng)理老李說(shuō):
- 這一塊功能有數(shù)據(jù)的時(shí)候就展示主卫,沒(méi)有就隱藏
- 這一塊挪到那塊下面
- 這一塊XXX功能刪了逃默,現(xiàn)在不需要了
- 之前刪的XXX功能補(bǔ)回來(lái)吧,然后改成放在XXX功能塊下面
- 這塊地方的高度根據(jù)內(nèi)容動(dòng)態(tài)設(shè)置
- ......(數(shù)不盡的小數(shù)點(diǎn))
程序猿懵了:
- 咦簇搅,我之前這個(gè)Section放的是什么完域,看界面的話不準(zhǔn)確,有些隱藏之類(lèi)的情況瘩将,界面上這塊東西對(duì)應(yīng)的是哪個(gè)Cell吟税,也不確定了。
- Delegate和DataSource姿现,少的時(shí)候只實(shí)現(xiàn)三四個(gè)方法肠仪,多的時(shí)候?qū)崿F(xiàn)七八個(gè),這些都和Section备典、Cell的各項(xiàng)信息有關(guān)异旧,這時(shí)候做什么變動(dòng)都需要改動(dòng)多處地方,if...else...也就寫(xiě)得到處都是提佣。
- ......(數(shù)不盡的小數(shù)點(diǎn))
這些麻煩不難解決吮蛹,就是有時(shí)候會(huì)很花時(shí)間,效率低拌屏,于是我想了一個(gè)方法解決它潮针。
最初是在做某一類(lèi)詳情的時(shí)候想出來(lái)的,后面經(jīng)過(guò)應(yīng)用到其他頁(yè)面和幾次變更的實(shí)踐之后發(fā)現(xiàn)倚喂,還真挺方便的每篷!下面將舉幾個(gè)例子來(lái)說(shuō)明這個(gè)方法及其特點(diǎn)。
本想將本文語(yǔ)言組織得有趣些务唐,但可惜功力不夠雳攘,時(shí)間也緊哈,下次再?lài)L試枫笛。
例子一
產(chǎn)品經(jīng)理老李說(shuō):“給我實(shí)現(xiàn)xxx功能吨灭,有5部分信息,其中A刑巧、C喧兄、D功能沒(méi)有東西的時(shí)候是隱藏的......”
程序猿答道:“哦......”
轉(zhuǎn)化成技術(shù)需求:
有五種Cell无畔,分別是ACell(根據(jù)data.a判斷隱藏與否)、BCell(默認(rèn)有)吠冤、CCell(根據(jù)data.c數(shù)組個(gè)數(shù)判斷隱藏與否浑彰,并且根據(jù)個(gè)數(shù)設(shè)置cell大小)拯辙、DCell(根據(jù)data.d數(shù)組個(gè)數(shù)判斷隱藏與否)郭变、ECell(默認(rèn)有),隱藏與否順序都是A\B\C\D\E涯保。
笨拙的實(shí)現(xiàn):
class XXXController: UICollectionViewController {
let data = ......
override func viewDidLoad() {
super.viewDidLoad()
collectionView.reloadData()
}
}
extension XXXController: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
// switch 或 if...else...
switch indexPath.row {
case 0:
// 判斷是A還是B
case 1:
// 判斷是B還是C還是D還是E......(心里罵著產(chǎn)品經(jīng)理)
// 這里很容易犯錯(cuò)诉濒,細(xì)想下你就知道了......(又被扣工資了囧)
case 2:
// 判斷是C還是D還是E......(心里罵著產(chǎn)品經(jīng)理)
case 3:
// 判斷是D還是E......(心里罵著產(chǎn)品經(jīng)理)
case 4:
// ECell
default:
// 還要寫(xiě)default......
}
}
}
extension XXXController {// UICollectionViewDelegate、UICollectionViewDataSource
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// 根據(jù)數(shù)據(jù)源計(jì)算分別有沒(méi)有A/C/D
// 一堆if...else......(心里罵著產(chǎn)品經(jīng)理)
return 2 + ......
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// 同sizeForItemAtIndexPath(心里罵著產(chǎn)品經(jīng)理)
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// 同sizeForItemAtIndexPath(心里罵著產(chǎn)品經(jīng)理)
}
}
優(yōu)雅的實(shí)現(xiàn):
enum XXXItemTypes {
case A
case B
case C(count:Int)
case D
case E
......
}
class XXXController: UICollectionViewController {
let data = ......
var itemTypes:[XXXItemTypes] = []
override func viewDidLoad() {
super.viewDidLoad()
configItemTypes(data)
collectionView.reloadData()
}
}
private extension XXXController {
func configItemTypes(data) {
itemTypes = []
if let _ = data.a {// 根據(jù)對(duì)象是否存在判斷隱藏與否的情況
itemTypes.append(XXXItemTypes.A)
}
itemTypes.append(XXXItemTypes.B)// 默認(rèn)有的情況
if let cCount = data.c.count where cCount > 0 {// 根據(jù)個(gè)數(shù)判斷隱藏與否的情況
itemTypes.append(XXXItemTypes.C(cCount))
}
if let dCount = data.d.count where dCount > 0 {
itemTypes.append(XXXItemTypes.D)
}
itemTypes.append(XXXItemTypes.E)// 默認(rèn)有的情況
......
}
}
extension XXXController: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
switch itemTypes[indexPath.row] {
case .A:
return ACell.cellSize(......)
case .B(let count):
return BCell.cellSize(count: count)
case .C:
return CCell.cellSize(......)
case .D:
return DCell.cellSize(......)
case .E:
return ECell.cellSize(......)
}
}
}
extension XXXController {// UICollectionViewDelegate夕春、UICollectionViewDataSource
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemTypes.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
switch itemTypes[indexPath.row] {
case .A:
......
case .B:
......
case .C:
......
case .D:
......
case .E:
......
}
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
switch itemTypes[indexPath.row] {
......
}
}
}
小分析:
可以看到未荒,所有的判斷(4處地方)都?xì)w結(jié)一處,也就是configItemTypes里面及志,省去了一堆因?yàn)殡[藏與否而做的判斷邏輯片排,而且不會(huì)出現(xiàn)復(fù)雜的判斷的邏輯出錯(cuò)。
例子二
又是產(chǎn)品經(jīng)理老李說(shuō):“D功能很重要速侈,放在第一處吧率寡,這很容易實(shí)現(xiàn)吧!”
程序猿答道:“嗯......”
笨拙的實(shí)現(xiàn):
每個(gè)UICollectionViewDelegate锌畸、UICollectionViewDataSource勇劣、UICollectionViewDelegateFlowLayout的方法里面的判斷邏輯都修改......
(磨刀霍霍向老李)
優(yōu)雅的實(shí)現(xiàn):
只需修改configItemTypes,將DCell的判斷邏輯放到最前面先判斷潭枣。(這是真的嗎?幻捏?就這么簡(jiǎn)單盆犁??)
小分析:
任何順序變化都可以通過(guò)簡(jiǎn)單修改configItemTypes而實(shí)現(xiàn)篡九,不改動(dòng)其他地方谐岁。
例子三
老李又來(lái)了:“把C功能去掉......”
程序猿答道:“嗯......”
笨拙的實(shí)現(xiàn):
同例子二,又要重新看那段判斷邏輯榛臼。
優(yōu)雅的實(shí)現(xiàn):
同樣只需修改configItemTypes伊佃,將添加CCell的部分刪除。(好崇拜自己芭嫔啤:饺唷!=鸬蟆K俊R樾健!O庇选K挂椤)
小分析:
任何增加和刪除都可以通過(guò)簡(jiǎn)單修改configItemTypes而實(shí)現(xiàn),不改動(dòng)其他地方醇锚。
進(jìn)階例子
上面的幾個(gè)例子都是沒(méi)有Section的哼御,如果再加多一層Section,那么復(fù)雜的程度就上升很多了焊唬,這里就不再贅述了恋昼。主要說(shuō)下這種情況下枚舉的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),可以有很多種求晶,下面是我的一個(gè)實(shí)現(xiàn)焰雕,還可以更優(yōu)化。
enum XXXSectionType {
case SectionA(rowTypes:[XXXRowType])
case SectionB(rowTypes:[XXXRowType], index: Int)
case SectionC(rowTypes:[XXXRowType])
}
enum XXXRowType {
case RowA
case RowB
case RowC
}
這種枚舉的方法在有section的情況下更彰顯了它的價(jià)值芳杏!依著上面的幾個(gè)例子的需求嘗試下就能體會(huì)得出來(lái)了矩屁。
總結(jié)
從上面的幾個(gè)需求和處理可以慢慢抽象出這麻煩的問(wèn)題所在:
=>Section/Cell的分布情況沒(méi)有統(tǒng)一
=>導(dǎo)致Delegate和DataSource的多處方法需要進(jìn)行判斷
=>導(dǎo)致展示變化時(shí)Delegate和DataSource的多處方法都要修改
=>導(dǎo)致修改辨識(shí)麻煩
=>導(dǎo)致效率低
枚舉的方法將所有分布情況都放在了分布數(shù)組的初始化上,Delegate/DataSource不需要通過(guò)indexPath去判斷自己當(dāng)前是哪個(gè)row/item爵赵,實(shí)際上就是將未知的麻煩的indexPath抽象出一維數(shù)組或者二維數(shù)組來(lái)表示它們的分布吝秕,并且每個(gè)分布情況所需的設(shè)置(如Cell高度、Cell樣式空幻、Header高度等等)都是固定的烁峭,所以任何變動(dòng)只需修改那個(gè)分布數(shù)組。
看似簡(jiǎn)單的抽取秕铛,卻處理掉了很多麻煩的問(wèn)題和冗余的處理约郁!
-END-
歡迎到我的博客交流:http://zackzheng.info