很多時候在app團隊作業時候會有這樣的場景:
- server 端小伙伴總是很忙,總是不愿意配合我們該接口
- 我們客戶端總需要重復搭建各類表單/表單型展示頁,每次都要在代碼里改datasource,改model等麻煩的事,哪怕框架共通寫的再好也要面對重復的代碼發呆
如果server的api返回的就是這樣的數據,那說明你遭遇的是正常待遇,那如果你說,唉?不對啊,我們有特地為app服務的專屬server團隊,那...你可以直接拉到下面看~
每當遇到這樣的場景,遭受正常待遇的我們總會想到設計下面這樣個model:
<pre>struct Person
{
var id : String!
var name : String!
var salary : Double = 0
var summary : String!
var description : String!
var displayedDiscription : String {
return "PersonId : \(id) \n Description : \(description)"
}
var displayedSalary : String {
return currencyGenerator(currencyDoubleValue: salary)
}
func currencyGenerator(currencyDoubleValue : Double) -> String
{
return ""
}
init(json : [String : Any]) {
}
}</pre>
然后為了完成上面右邊的View的需求,我們會見個tableview,然后為這個親愛的tableview做個特殊的datasource,比如這樣:
<pre>func setupData() -> [String : Any]
{
let sampleSource = ["id" : "10000", "name" : "Fish", "salary" : 5000 , "summary" : "fff", "description" : "sss"] as [String : Any]
let person = Person(json : sampleSource)
return ["Name" : person.name,
"Salary" : person.displayedSalary ,
"Summary" : person.summary ,
"Desciprtion" : person.displayedDiscription]
}</pre>
后續的就不看了,大家也都猜得出。
我們辛辛苦苦做完了,上線了結果隔壁產品同學說,
呀,我們想加個字段,加一行手機號之類的東西,
呀,我們還想拿掉那個summary,感覺沒什么用,
哎呀呀,我們還要調整個順序,把Salary放到最下面...
呀呀呀,呀你妹啊 T_T 你以為這改動很簡單么,要加字段,要加xxx,要發版本上appstore...
那回過頭想想每次這樣做,我們的心會不會很累?可能server 端的同學會覺得這就是我們要做的事,可是重復寫這些code,每次要改那么多分散的地方,我們會不會很累?我們不覺得累,我們的xcode都會累呢。
所以我們靜下心來看下上面那個View,其實我們可以在每行都找到共通特征:
比如左邊有個title,右邊有個value,
那我們是不是可以有個Row的對象,里面有2個屬性:key和value,
key就是左邊顯示的文案,value是動態變化的
所以有了這樣的對象:
<pre>struct ConfigRow
{
// left title
var key : String!
// right title
var value : String!
}</pre>
那是不是如果我們有了5個這樣的對象,就能滿足上面那個View里的結果了呢,當然你可以選擇創建5個ConfigRow,然后湊成個數組作為datasource。但是這個做法,依舊很笨,依舊要改代碼;
這時候,傳說中的plist就要登場了,
我們是不是可以把這5個對象放在plist中,然后做一系列反序列化的方法把plist構建成我們要的數組datasource是不是就好了呢?因為這一系列方法肯定是共通的,所以每次做新類似內容,我們只需要建立個新的plist,然后調用共通方法構建datasource,然后調用應該已經可能被共通抽離出來的tableview構建方法,這樣就能完成這樣個簡單模塊的編寫了。
最重要的是,每次有文案修改,你只需要修改plist中內容就行了,這樣哪怕是別的不是負責你這個模塊的開發者來協助,他也能很快完成,畢竟字他總認識~
但是,我們需要對上面的Row進行下擴展,我們還需要加上顏色和排序的屬性,來隨意控制相應的屬性:
<pre>struct ConfigRow
{
// left title
var key : String!
// right title
var value : String!
var sortOrder : Int = 0
var color : String = ""
}</pre>
當然你還可以新增別的,比如左邊title的樣式屬性,右邊的value的樣式屬性等等等。
這樣,一個白板就構建完了,我們的tableView上已經華麗的自動出現了那些固定文案,比如左側的所有label,或者順序/顏色;但是,最重要的右邊文案從哪來填充呢?
這里提供個最簡單的path方式,我們在plist中每個row的displayValue的默認值寫成對應的json中的key值,比如name的displayValue就填value,然后在cell的設置row的方法中:
<pre>leftTitle.text = model.value(forKey : row.displayValue)</pre>
這樣的思路就能解決白板填充的問題了,如果需要存下值的化,我們最終的ConfigRow可以變成這樣:
<pre>struct ConfigRow
{
// left title
var key : String!
// right title
var value : String!
// json中對應的key值
var valuePath : String!
var sortOrder : Int = 0
var color : String = ""
}</pre>
valuePath就是我們可以寫死的key值,而從json中獲取來的數據可以塞到value中,這樣保存著也方便以后用
再擴展下,比如這是個填寫型表單頁
也是一左一右的構造,只是右邊是個輸入框了,我們針對輸入框做了一系列屬性來控制,比如
- 右邊框的樣式,可能是輸入,也可能是日歷,也可能是選擇,這些通過枚舉后在共通框架中實現就行了
- 輸入限制的正則表達式regex
- 輸入框鍵盤的樣式:數字/字母等
同時我們把單個Row的cell相應屬性也抽到了plist中修改:
- cell的identifier重用標示,其實也就等于你可以指定當前Row用什么樣的cell來顯示
- cell的segue,因為我們用的是storyBoard跳轉,所以可以通過segueIdentifier來直接在tableView的didSelect方法中進行跳轉,當然別的跳轉方式也可以支持喲,就看需求了
- cell的高度height,如果不需要自動計算的或者手動計算的話,我們直接把cell的高度也放在了ConfigRow中進行修改,這樣controller中寫死的東西又少了
當然還有別的功能就不一一列舉了。
其實這一整套是很簡答的思路設計,重點核心就是我們去掉了業務對象化的思路,而是采用了針對View的單行進行對象化,即原本縱向的考慮變成了橫向,真正站在tableView的角度想他需要什么。
這樣的設計可以把我們從修改代碼的常規邏輯中抽離出來,其實每次我們只需要改相應的plist,也能完成看似很簡單其實真的很簡單的問題,何不嘗試一下呢?
當然,之前提到,如果你有一個很好的專門服務的server服務團隊,這套plist就可以放在服務端,這樣他們也可以通過配置文件來直接配置表單了;而server端哪怕沒有這個條件,我們也可以把這些配置放在我們工程中,這樣每次開發時候也方便我們修改了。
當然,這期中最有問題的應該就是上面的valuePath這一段了,如果遇到復雜的需求需要同一個位置顯示多個字段拼接的,比如:
Description = ID + Descprition
的顯示該怎么辦呢?
下期中會介紹專門為這個需求而誕生的工具 CodingForP,
https://github.com/SpiciedCrab/CodingForP
到時候我們就能明白這有多有趣了。