4.%d SwiftUI — List

春麗 S.T.E.M.
8 min readJul 22, 2021

--

目錄

⦿ View
⦿ SwiftUI 專案
⦿ Label & TextField
⦿ Image
⦿ Button
⦿ List
⦿ 與 UITableView 的差異

View

先看到如下:

在 UIKit,我們常使用 TableView 去建構整頁資訊,如果這個資訊是一個 Table,資訊便會是一格一格的,這個格子在 UIKit 裡叫做 TableViewCell,如果 Table 是一千筆資料,在 UIKit 中,能夠 Reuse 一個基本的 Cell,讓 VC 只將能夠呈現在頁面上的 Cell 產生,而不去產生其他 Cell,當原來的 Cell 在滑動時消失頁面上,便可拿去顯示其他要出現在頁面的 Cell,這即是 dequeueReusableCell 的作用。

通常我們會用 VC 加上 TableView 去建構動態的表格,當然你也可以使用 TableViewController,確保該 VC 只需要 Table 而沒有其他操作按鈕,不然在 TableViewController 加入其他 UIElement 就相對麻煩。

在 SwiftUI 中,表格的呈現我們用的是 List。

繼續閱讀|回目錄

SwiftUI 專案

Label & TextField

首先,創建一個新的專案,Language 選擇 Swift,Life Cycle 是 UIKit Delegate。

往後,你就加入 SwiftUI 的 View,又或者在建立好 Swift 檔案,導入 SwiftUI 框架,如下:

由於我們需要一個頂部夾帶 Label 的 TextField,所以先創造出右圖的原型,而這個 LabelTextField 需要兩個變數,一個是 Label 的 text(String),一個是 TextField 的 placeHolder 的 text(String),可以想見,Label 與 TextField 是用 VStack 包起來的。

為了讓 Label 文字朝左對齊,我們設定 VStack 的參數 alignment 為 .leading。接著看到 Previews 的地方:

struct LabelTextField_Previews: PreviewProvider {

static var previews: some View {
Group {
LabelTextField(labelText: "Name",
placeHolder: "Fill in your name")
LabelTextField(labelText: "Gender",
placeHolder: "Fill in your gender")
LabelTextField(labelText: "Address",
placeHolder: "Fill in your address")
LabelTextField(labelText: "Phone",
placeHolder: "Fill in your phone number")
}
}
}

我們可以把需要的欄位放到 Group 中,在 Canvas 就可以看到:

若把 Group 改成 VStack 就會呈現上面右邊的結果,很好!右邊就是我們要的基本欄位了。

Image

Image 的使用也相當簡單,如下:

Button

Button 也不難,如下:

而最終我們希望把 Image、Label、TextField 跟 Button 組合在一起,最終形成一個 List。

繼續閱讀|回目錄

List

我們希望的組成是一個 PortraitImage、四個 LabelTextField,作為一個單位,呈現六筆資料,再加上一個 SendedButton,一個單位如下:

然而這種寫法並沒有使用到 List,並且僅呈現一筆資料,在 Previews 的地方,如果呈現了六筆資料,並且只用到 ContentView(),而沒有另外用 VStack 包著各筆 Table 的資料,那麼便成功了。

然而在 VStack 的地方,我們竟然用了那麼多的 LabelTextField,這實在不好看,所以改成如下:

struct TextData: Identifiable {
var id: String? = nil
var one: String
var two: String
}

let textDatas = [
TextData(one: "Name", two: "Fill in your name"),
TextData(one: "Gender", two: "Fill in your gender"),
TextData(one: "Address", two: "Fill in your address"),
TextData(one: "Phone", two: "Fill in your phone number")
]

ForEach(textDatas) { text in
LabelTextField(labelText: text.one,
placeHolder: text.two)
}

用一個 ForEach 便搞定,在這個 APP 中,因為這些欄位是固定的,不同的資料是 PortraitImage,所以,在 List 中應該是下面這樣:

         let employeeDatas = employeeData

List(employeeDatas, id: \.self) { data in
PortraitImage(imageName: data.image)
.padding(EdgeInsets(top: 8,
leading: 8,
bottom: 8,
trailing: 8))

ForEach(textDatas) { text in
LabelTextField(labelText: text.one,
placeHolder: text.two)
}
}

特別注意要使用 ForEach,這個型別必須遵循 Identifiable,要使用 id: \.self 來識別項目,這個型別必須遵循 Hashable,如果要能夠解析,這個型別還必須遵循 Decodable。

完成如下:

繼續閱讀|回目錄

與 UITableView 的差異

UIKit 是命令式(Imperative)的方法,SwiftUI 則是宣告式(Declarative)的方法,UIKit 的 UITableView 可以拿來與 SwiftUI 的 List 對比。

UITableView 需要透過遵循 UITableViewDataSource 去提供資料來顯示,但 List 是直接綁定 Data,自動生成該有的表格。

不過在 Cell 的 Reuse 兩者皆有,只不過 SwiftUI 不用顯性地去使用 dequeueReusableCell,並且 SwiftUI 整合了某些特性,不用再顯性地去使用原先在 UIKit 中欲實現必須使用的方法,例如重新排列 Cell 的順序,在 UIKit 中,所謂 reordering 的問題。

我們知道 UIKit 是比較外顯地操作,目的性與邏輯較強,然而 SwiftUI 早已做好了 Data Binding,UI 跟 Data 是綁在一起呈現的,在 UIKit 中必須自己實作這一塊,關鍵字:Boxing、RxSwift、Combine。

這次就分享到這,感謝您的閱讀。

繼續閱讀|回目錄

附上 GitHub:

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet