PJBreedsViewController 元件開發總結
這是我另外一個專案其中一個元件——品種選擇器,因為今天是週六,磨磨唧唧的造出了它。一眼看過去跟現有的通訊錄樣式和操作方式基本一致,但大家也知道我的尿性,從最開始能用三方元件就用三方到現在能自己寫就自己寫。
因為前後端都是我自己一個人在做(創業狗就是慘 = =)所以在今天萌生了很多好玩的想法,剛把這個組建弄出來後覺得有必要跟大家分享一些好玩的事情。
UI
先來看看 UI 樣式,
最開始看到設計圖時,並不認為這是一個有多少搞頭的東西,一直拖到今天。這是最終實現的成果,
思考(一)
給到我的文案是個 .docx 格式的文件,如下所示:
之前溝通過了一次,給我按照字母表順序排好就行了。最開始我的設計非常簡單,因為後端是用 python 寫的,直接從檔案中讀出資料,split
id
和 zh_name
即可,遂開幹。
實踐(一)
# 初始化:儘量通過 python shell 呼叫該方法
def init_dog_breed():
f = open(settings.DOG_BREED_DIR, 'r')
f_str = f.read()
f_str_arr = f_str.split()
for dog_name in f_str_arr:
dog_breed(zh_name=dog_name).save()
f.close()
複製程式碼
從本地路徑讀取轉化成 .txt
# 新增狗品種
def add_dog_breed(breed_name):
dog_breed(zh_name=breed_name).save()
複製程式碼
當然,也會有貓的,因為基本上差不多就不展開了。介面上這麼寫:
@decorator.request_methon('GET')
@decorator.request_check_args(['pet_type'])
def get_breeds(request):
pet_type = request.GET.get('pet_type', '')
functions = {
'dog': dog(),
'cat': cat()
}
if pet_type in functions.keys():
json = {
'breeds': functions[pet_type]
}
return utils.SuccessResponse(json, request)
else:
return utils.ErrorResponse('2333', '不支援該物種', request)
# 獲取所有狗品種
def dog():
dog_breeds = dog_breed.objects.all()
breeds = []
for breed in dog_breeds:
json = {
'id': breed.pk,
'zh_name': breed.zh_name,
}
breeds.append(json)
return breeds
複製程式碼
猜測後續產品可能還會引入其它寵物,畢竟現代人對寵物的需求是越來越奇葩了,沒有直接 if-else
,想用 switch
,但發現 python 中並沒有 switch
語句,查閱一番資料後,發現居然可以用 key-value
完成,雖然有些稍許麻煩,但第一次見還可以把鍵值對玩成這樣!
訪問對應介面後拿到的 JSON 格式資料如下:
{
"msgCode": 666,
"msg": {
"breeds": [
{
"id": 89,
"zh_name": "拉布拉多尋回犬"
},
{·
"id": 90,
"zh_name": "拉薩犬"
},
{
"id": 91,
"zh_name": "臘腸犬"
},
{
"id": 92,
"zh_name": "蘭波格犬"
},
{
"id": 93,
"zh_name": "獵水獺犬"
},
]
}
}
複製程式碼
一切順利,看起來不錯,開始造客戶端 UI。客戶端上的實現同樣也是比較輕鬆,一個 tableView
的正常渲染流程即可。
資料渲染出來後,腦子已經在快速運轉,站起來活動活動,發現肚子有些餓,糾結了一會是食堂呢還是餓了麼,最後因為貧窮而選擇了食堂。
思考(二)
午飯結束後,繼續幹活。開始做資料分組,思考並發現了問題所在,如果按照上午介面所返回的資料格式去做,那麼就需要端上做資料分組,把寵物品種按照 A~Z
的順序放到一個個的 section
中,這樣不但 iOS 需要做一遍,以後 Android 也要再做一遍,而且極其有可能還是我寫,本來我就十分厭煩 Android,多花費一分鐘甚至一秒鐘都是極其不樂意的。
所有,重新思考介面返回的資料格式。可以確保的是,資料都已經按照字母序排好了,我們只需要對資料做分組,把第一個字的拼音的第一個字母相同的品種歸類為一組,最後把所有組都放到一個大的列表中,序列化為 JSON 返回即可完事。
遂又開幹!
實踐(二)
首先給品種模型新增了一個欄位 group
用於標記所屬組別,中途考慮到了不想多增遷移檔案,居然腦殘的把之前生成的表給刪了,導致後邊生成遷移檔案時對不上,最後又刪庫重來,真是多此一舉 = =。
重新把基本操作都弄完後,改造初始化資料的方法,用到了一箇中文轉拼音的庫 pinyin
:
# 初始化:儘量通過 python shell 呼叫該方法
def init_dog_breed():
f = open(settings.DOG_BREED_DIR, 'r')
f_str = f.read()
f_str_arr = f_str.split()
# 刪除 array 中的第一個 'A'
del f_str_arr[0]
group = 'A'
for dog_name in f_str_arr:
first_cat_name = pinyin.get(dog_name, format='strip')[0:1].upper()
if first_cat_name != group:
group = first_cat_name
# 切換 group 時跳過
continue
dog_breed(zh_name=dog_name, group=group).save()
f.close()
複製程式碼
這樣清洗過資料後,資料就十分清晰漂亮了:
+-----+-------+--------------------------------+
| id | group | zh_name |
+-----+-------+--------------------------------+
| 1 | A | 阿富汗獵犬 |
| 2 | A | 阿拉斯加雪橇犬 |
| 3 | A | 愛爾蘭梗 |
| 4 | A | 愛爾蘭紅白雪達犬 |
| 5 | A | 愛爾蘭獵狼犬 |
| 6 | A | 愛爾蘭軟毛梗 |
| 7 | A | 愛爾蘭水獵犬 |
| 8 | A | 愛爾蘭峽谷梗 |
+-----+-------+--------------------------------+
複製程式碼
而介面,只需要進行拼接同類資料即可,
# 獲取所有狗品種
def dog():
dog_breeds = dog_breed.objects.all()
# 所有種類
breeds = []
# 當前種類名
breed_groups = []
group = "A"
for breed in dog_breeds:
if breed.group != group:
breed_group = {
'group': group,
'breeds': breed_groups,
}
breeds.append(breed_group)
group = breed.group
breed_groups = []
b_group = {
'id': breed.pk,
'zh_name': breed.zh_name,
}
breed_groups.append(b_group)
return breeds
複製程式碼
這樣,客戶端就能夠拿到已經分組好的資料:
{
"msgCode": 666,
"msg": {
"breeds": [
{
"group": "A",
"breeds": [
{
"group": "T",
"breeds": [
{
"id": 137,
"zh_name": "田野小獵犬"
}
]
},
]
},
{
"group": "W",
"breeds": [
{
"id": 138,
"zh_name": "玩具獵狐梗"
},
{
"id": 139,
"zh_name": "玩具曼徹斯特犬"
},
]
}
]
}
}
複製程式碼
那客戶端接下來要做的事情稍微冗餘一些,但不復雜。首先先確定 tableView.sections
的值,然後返回 sectionHeaderView
,接著編寫 cellForRow
渲染 cell 的方法,依然是正常的 tableView
渲染流程。
剩下的就是一些其它 UI 和互動細節上的修修補補了。
思考和總結
這次做的這個元件前後端花費的時間比例大約在 7:3,主要時間都花在客戶端上,因為是第一次做類似於這種通訊錄元件的開發,再加上是週六,讓自己的大腦和心情都放鬆了下來,沒有把時間抓得特別緊。
給我最大的收穫是最開始只考慮了後端處理資料的便利,而忘了前端處理資料的複雜,到後邊轉換了思維,用前端的思維對介面格式進行了修改,這一來一回讓自己更加明白了前後端配合是才能夠把一個東西做好,做到極致。
原文地址:PJ 的 iOS 開發之路