Swift使用enum抹平數(shù)組元素差異實(shí)例詳解
前言
通過(guò)Protocol去封裝入?yún)ⅲㄆ搅巳雲(yún)⒅g的差異。
今天這篇依然圍繞一個(gè)我遇到的業(yè)務(wù)場(chǎng)景,給大家提供一種思路——使用enum抹平數(shù)組元素差異。
業(yè)務(wù)場(chǎng)景
我先說(shuō)明一下業(yè)務(wù)場(chǎng)景:
頁(yè)面是一個(gè)有限可以滑動(dòng)的的頁(yè)面(后面我們會(huì)分析到其實(shí)無(wú)限或者有限都無(wú)所謂)
頁(yè)面的每一個(gè)子View都是和后臺(tái)返回的數(shù)據(jù)綁定,其JSON大致可以理解為下面這種形式:
{ "aPart":{ "some":"" }, "bPart":[ { "label":"", "value":"" }, { "label":"", "value":"" } ], "cPart":[ { "iconUrl":"", "text":"", "functionType":"" }, { "iconUrl":"", "text":"", "functionType":"" } ], "dPart":{ "name":"", "age":"" }, "deviceType":"", "serviceAble":"" }
業(yè)務(wù)需求如下:
- aPart對(duì)應(yīng)aView,bPart對(duì)應(yīng)bView,cPart對(duì)應(yīng)cView,dPart對(duì)應(yīng)dView,可以如此窮舉下去
- 每個(gè)part的JSON結(jié)構(gòu)可能都不相同
- 后臺(tái)下發(fā)JSON的時(shí)候,會(huì)根據(jù)用戶賬號(hào)的情況,返回不同數(shù)據(jù),比如aPart的業(yè)務(wù)沒(méi)有,后臺(tái)會(huì)返回
"aPart":null
,App的aView需要隱藏,存在可能多個(gè)part都返回為null的情況,比如"bPart"
和"cPart"
都為null的情況,那么App的bView和cView需要隱藏。
那么問(wèn)題來(lái)了,iOS端如何構(gòu)建一個(gè)可以靈活配置的界面?
用什么控件
使用UIScrollView的分析
作為開(kāi)發(fā)App頁(yè)面的第一點(diǎn),選取合適的控件是非常重要,因?yàn)榭丶Q定了最終數(shù)據(jù)源的形式。
因?yàn)楹笈_(tái)數(shù)據(jù)返回的并不是一個(gè)JSON數(shù)組,同時(shí)又是有限的數(shù)據(jù),很多人優(yōu)先會(huì)考慮通過(guò)UIScrollView
去進(jìn)行頁(yè)面的構(gòu)建,我一開(kāi)始也是這么想的,但是麻煩的是這一點(diǎn):
后臺(tái)下發(fā)JSON的時(shí)候,會(huì)根據(jù)用戶賬號(hào)的情況,返回不同數(shù)據(jù),比如aPart的業(yè)務(wù)沒(méi)有,后臺(tái)會(huì)返回"aPart":null
,App的aView需要隱藏。
也就是說(shuō)你把aView
、bView
、cView
、dView
貼在了scrollView上面之后,需要根據(jù)后臺(tái)的數(shù)據(jù)隱藏頁(yè)面,甚至更新布局邏輯,每一個(gè)view都有2種情況,假設(shè)后臺(tái)有4個(gè)業(yè)務(wù)數(shù)據(jù),那么那就是2的4次方——32種可能。
其實(shí)僅隱藏還是顯示非常簡(jiǎn)單,但是如果是使用SnapKit
布局,那么更新view的布局,可能就并不是特別好了,同時(shí)如果這個(gè)頁(yè)面后續(xù)還有新的業(yè)務(wù)數(shù)據(jù),那么就子view會(huì)繼續(xù)增加,維護(hù)成本也會(huì)越來(lái)越高。
所以,到此使用UIScrollView的方案,別否決了,并不是說(shuō)它不能構(gòu)建,而是成本有些高,而且不夠靈活。
所以剩下的只剩下一種選擇了——使用通過(guò)數(shù)據(jù)源綁定UI的UITableView
。(備注:其實(shí)使用UICollectionView
和UITableView
都一樣,只是多一個(gè)瀑布流布局而已)。
使用UITableView的分析
使用UITableView
的優(yōu)勢(shì):
完完全全通過(guò)數(shù)據(jù)去驅(qū)動(dòng)頁(yè)面,頁(yè)面是否顯示完全通過(guò)有無(wú)數(shù)據(jù)決定。
但是數(shù)據(jù)源相較普通模型有著更加嚴(yán)格的數(shù)據(jù)格式——數(shù)組!
而且數(shù)組的每個(gè)元素最好都是相同的數(shù)據(jù)類型,因?yàn)槿绻褂?code>[Any]這樣去表達(dá)一個(gè)數(shù)據(jù)源,成本太高。
好了,既然我們定下了使用UITableView
來(lái)進(jìn)行頁(yè)面構(gòu)建,那么剩下的難點(diǎn)也就來(lái)了——如何將后臺(tái)的數(shù)據(jù)加工成為一個(gè)好用的數(shù)據(jù)源?
加工數(shù)據(jù)
將后臺(tái)數(shù)據(jù)加工成為一個(gè)數(shù)組并不難,關(guān)鍵是統(tǒng)和數(shù)據(jù)類型才是難點(diǎn)。
在上一篇文章里面,我通過(guò)Protocol去統(tǒng)和了Model
與[String: String]
,在這次情況下面可不可行呢?
protocol EraserConvertible {} struct Response: Codabel { let aPart: APart? let bPart: BPart? let cPart: CPart? let dPart: DPart? } struct APart: Codabel, EraserConvertible {} struct BPart: Codabel, EraserConvertible {} struct CPart: Codabel, EraserConvertible {} struct DPart: Codabel, EraserConvertible {}
我們回頭看看這個(gè)JSON,你不得不認(rèn)清這樣個(gè)現(xiàn)實(shí),每個(gè)Part都是獨(dú)立的,完全看不到任何關(guān)聯(lián),如果想要做類型一致,EraserConvertible
這個(gè)協(xié)議很難滿足。
不如做向上類型統(tǒng)和,也就是說(shuō)數(shù)據(jù)源變成let dataSource = [Response]()
這種形式,在tableView的數(shù)據(jù)源方法中獲取單個(gè)數(shù)據(jù),然后在進(jìn)行分析:
let dataSource = [Response]() func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item = dataSource[indexPath.row] if let aPart = item.aPart { /// 構(gòu)建aCell let cell = tableView.dequeueReusableCell(withIdentifier: aCell.className)! /// 將aPart賦值給aCell cell.aPart = aPart return cell } if let bPart = item.bPart { /// 構(gòu)建bCell, let cell = tableView.dequeueReusableCell(withIdentifier: bCell.className)! /// 將bPart賦值給bCell cell.bPart = bPart return cell } . . . }
嗯,這樣看起來(lái)非常不錯(cuò),只是每個(gè)item都包含了aPart到dPart,方案的是可行。讓我們想想有沒(méi)有更加優(yōu)雅的思路呢?
數(shù)據(jù)源的類型不同決定了不同的Cell。我們可不可以用狀態(tài)來(lái)表示?
于是乎我寫(xiě)下了這樣的代碼:
enum BusinessPart { case a case b case c case d }
數(shù)據(jù)源變成let dataSource = [BusinessPart]()
這種形式,那么如何區(qū)分每個(gè)case
不同的數(shù)據(jù)呢?
Swift的enum是可以帶參數(shù)的,而且單個(gè)case帶不帶參數(shù),帶什么類型的參數(shù)都很自由。
于是乎,我們接著改造BusinessPart
:
enum BusinessPart { case a(APart) case b(BPart) case c(CPart) case d(DPart) }
這樣我現(xiàn)在就通過(guò)enum
抹平的數(shù)組元素的差異,接下來(lái)只要把后臺(tái)數(shù)據(jù)架構(gòu)成為我想要的[BusinessPart]格式就好了,這里放處理邏輯:
private func process(model: Response) -> [BusinessPart] { var array: [BusinessPart] = [] /// 非空才加入數(shù)組,后臺(tái)返回null,此時(shí)aPart為nil,那么aCell也不會(huì)出現(xiàn) if let aPart = model.aPart { array.append(.a(aPart)) } if let bPart = model.bPart { array.append(.b(bPart)) } if let cPart = model.cPart { array.append(.c(cPart)) } if let dPart = model.dPart { array.append(.d(dPart)) } return array }
在TableView
的數(shù)據(jù)源方法中這么使用:
let dataSource = [BusinessPart]() func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let item: BusinessPart = dataSource[indexPath.row] switch item { case .a(let aPart): /// 構(gòu)建aCell let cell = tableView.dequeueReusableCell(withIdentifier: aCell.className)! /// 將aPart賦值給aCell cell.aPart = aPart return cell case .b(let bPart): /// 構(gòu)建bCell let cell = tableView.dequeueReusableCell(withIdentifier: bCell.className)! /// 將bPart賦值給bCell cell.bPart = bPart return cell case .c(let cPart): /// 構(gòu)建cCell let cell = tableView.dequeueReusableCell(withIdentifier: cCell.className)! /// 將cPart賦值給cCell cell.cPart = cPart return cell . . . } }
至于這個(gè)[BusinessPart]
的dataSource怎么在UITableViewDataSource
中如何處理,亦或者在BusinessPart
的分類中怎么處理,這個(gè)就是一個(gè)仁者見(jiàn)仁智者見(jiàn)智的問(wèn)題了。
總結(jié)
到最后,這個(gè)頁(yè)面的數(shù)據(jù)加工,最后還是變成了向上還是向下統(tǒng)和的思考。
其實(shí)就是對(duì)于一個(gè)差異性特別大的數(shù)據(jù),如何比較好的整合為一個(gè)在iOS中合適的數(shù)組問(wèn)題,通過(guò)字段的全覆蓋達(dá)到模型的統(tǒng)合,抑或通過(guò)Swift中枚舉帶參的特點(diǎn),抹平差異。
從代碼層面上看,兩者的代碼量差不多,但是使用enum抹平數(shù)組元素差異為我們提供一個(gè)新的解題思路,至少這是我自己思考的成果。
另外一個(gè)角度就是通過(guò)布局控件來(lái)展開(kāi),因?yàn)槲铱碅ndroid就是直接用一個(gè)ScrollView擼起的:
Android開(kāi)發(fā)中,大部分控件都有visibility這個(gè)屬性,其屬性有3個(gè)分別為“visible ”、“invisible”、“gone”。主要用來(lái)設(shè)置控制控件的顯示和隱藏。
invisible當(dāng)控件visibility屬性為invisible時(shí),界面保留了view控件所占有的空間;而控件屬性為gone時(shí),界面則不保留view控件所占有的空間。
因?yàn)锳ndroid這個(gè)三個(gè)屬性可以非常便利的完成隱藏還是不隱藏,以及是否保留控件所占有的空間。
而iOS可能需要不僅改變isHidden屬性,甚至連view的frame也要進(jìn)行改變,成本太大了。在使用UITableView的分析已經(jīng)提到。
但是也不是不可能,比如使用FlexLib應(yīng)該可以實(shí)現(xiàn)。(備注:我自己沒(méi)用過(guò)FlexLib庫(kù),這個(gè)有待考證哈??),但是也增加了一定的學(xué)習(xí)成本與引入了更多的庫(kù)。
參考文檔
以上就是Swift使用enum抹平數(shù)組元素差異實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift enum抹平數(shù)組元素差異的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Swift實(shí)現(xiàn)可自定義分頁(yè)寬度的UIScrollView
這篇文章主要為大家詳細(xì)介紹了Swift實(shí)現(xiàn)可自定義分頁(yè)寬度的UIScrollView,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07SwiftUI 登錄界面布局實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了SwiftUI 登錄界面布局實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Swift項(xiàng)目中利用SWRevealViewController實(shí)現(xiàn)側(cè)滑菜單
這篇文章主要介紹了Swift項(xiàng)目中利用SWRevealViewController實(shí)現(xiàn)側(cè)滑菜單,需要的朋友可以參考下2015-12-12Swift編程中實(shí)現(xiàn)希爾排序算法的代碼實(shí)例
希爾排序是對(duì)插入排序的一種改進(jìn)版本,算法本身并不穩(wěn)定,存在優(yōu)化空間,這里我們來(lái)講一下希爾排序的大體思路及Swift編程中實(shí)現(xiàn)希爾排序算法的代碼實(shí)例2016-07-07