建立第一個 SwiftUI App:Day 5 – TextField, @State

Photo by Benjamin Sow on Unsplash
Photo by Benjamin Sow on Unsplash
我們把新增商品的頁面分為兩個章節。第一章我們會介紹 VStack、Button 和 TextField 等 UI 元件,另外還會介紹 @State。在上一章已經學了一些 UI 元件,所以這章的 UI 元件部分會容易多。SwiftUI 的 @State 是全新概念,務必要多花一點時間了解。

我們把新增商品的頁面分為兩個章節。第一章我們會介紹 VStackButtonTextField 等 UI 元件,另外還會介紹 @State。在上一章已經學了一些 UI 元件,所以這章的 UI 元件部分會容易多。SwiftUI 的 @State 是全新概念,務必要多花一點時間了解。

Coffee Shop 的完整程式碼可以在 下載。

新增商品頁

Coffee Shop 的新增商品頁面如下圖。有商品名稱和價格的輸入框,和一個新增按鈕。

新增商品頁面

在 Product 資料夾下新增一個 SwiftUI 檔案叫 AddProductView.swift。貼上以下的程式碼。

struct AddProductView: View {
    @State var name: String = ""
    @State var price: String = ""

    var body: some View {
        VStack {
            HStack {
                Text("Product name:")
                TextField("Input product name", text: $name)
                    .textFieldStyle(RoundedBorderTextFieldStyle())  // 設定輸入框的邊框為圓角樣式
            }
                .padding()

            HStack {
                Text("Price:")
                TextField("Input price", text: $price)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .keyboardType(.decimalPad)  // 設定輸入框的鍵盤為數字鍵盤
            }
                .padding()

            Button(action: {
                print("on add clicked")
            }, label: { Text("Add") })
                .padding()
                .frame(maxWidth: .infinity)
                .background(Color("ButtonColor")) // 背景顏色
                .foregroundColor(Color.white)  // 前景顏色,也就是字的顏色
                .cornerRadius(30) // round border 為 30
                .font(.headline)  // 字體樣式
                .padding()

            Spacer()
        }
    }
}

struct AddProductView_Previews: PreviewProvider {
    static var previews: some View {
        AddProductView()
    }
}

整個版面是一列一列地垂直排列下去,所以我們用 VStack 做最外層的 Layout 管理容器。HStackLabel 已經在商品列表中有講解過,所以我們這裡不在重述。

TextField 是輸入欄位。第一個參數是 Placeholder,就是提示字串。使用者輸入後的字串,會放到第二參數的變數中。詳細的語法講解等等下面會說明。

HStack 後面有接一個 .padding(),讓 HStack 和四周有些空間。這樣比較不會擁擠。

Button 是按鈕。第一個參數是按鈕被按下後,會觸發的動作。目前我們只是印出一個字串代表有接收到事件 (Event),詳細的實作會在下個章節。第二個參數是按鈕的 UI 顯示。

Botton 下面有很多 UI 相關的設定,其中 .frame(maxWidth: .infinity) 是要讓 Button 的寬度可以撐到最大。

最後面有個 SpacerVStack 預設是將它的 Child Views 置中 (Align Center)。但是我們希望表格是置頂 (Align Top)。所以在最後面加上一個 Spacer,他會將高度撐到最大,把上面的 Child Views 推到頂部。

Color Set

Botton 下面有 .background(Color("ButtonColor")),這是背景顏色設定。Color 中有很多已經預先定義好的顏色,如 .foregroundColor(Color.white) 就是直接使用預先定義好的白色來設定前景。但是我們要的顏色並沒有被預先定義。

Xcode 提供一個方式讓我們可以自定義顏色,就是 Color Set。點選 Assets.xcassets -> New Color Set。

新增 Color Set
新增 Color Set

因為程式中是 Color(“ButtonColor”),所以將 Color Set 命名為 ButtonColor。然後依造下圖將顏色設定為 #D06976。然後在程式中就可以用 Color(“ButtonColor”) 來取得 ButtonColor 對應的顏色。

設定 Color Set 顏色
設定 Color Set 顏色

如果你仔細看一下 Color ,會發現它有很多建構子。其中有可以直接傳入 RGB 色碼,這樣就不需要建立 Color Set。但是這麼做的話,色碼會散落在程式中。同一個色碼可能會在程式中多次被使用。當你要一起改色碼的話,那就很令人頭痛了。

@State

AddProductView 中有兩個 Property,分別是 nameprice。它們前面有 @State。符號 @ 表示 State 是一個 Property Wrapper。Property Wrapper 是 Swift 5 之後才有的新語法。

當一個 Property 宣告為 @State 時,我們說這個 Property 是個 State,而 SwiftUI 會管理這個 Property 的值。當 State 被改變時,整個 AddProductView 就會被 Invalidate (設為無效),也就導致 AddProductView 會更新並重畫。

為何 SwiftUI 可以偵測到 State 的值被改變呢?因為當 Property 被宣告為 State 時,State 會對這個 Property 做一個包裝 (Wrap)。所以程式不是直接改變 Property 的值,而是透過 State 來改變 Property 的值。這也是為何 @State 是個 Property Wrapper。

當設定一個字串給 AddProductView.name 時,不是直接設定給它。而是透過 State 包裝的函式來設定 name 的值,並在這函式中告訴 name 所屬的 View 要更新。而 Swift 將這些包裝細節都隱藏起來了。

當你要把 State 當參數傳給另外的 View 時,就要用 $ 當前綴,像 $name。符號 $ 叫做 Projected View,也是 Swift 5 的新東西。

這樣你就了解程式中 TextField 的第二個參數,我們傳 $name 而不是 name。當你在 TextField 中輸入字串,TextField 就會直接將字串放到 $name。然後 View 就會更新。

@State 和 $ 常像這樣一起搭配使用。用起來的感覺像是網頁開發中的雙向綁定 (two-way binding)。更詳細的解說,可以參考下面這篇文章。

結語

SwiftUI 的 @State 讓我們開發 UI 時更加地方便。當 State 值改變時,View 會自動更新。如果有開過 UIKit 的話,就可以了解它所帶來的方便。Color Set 可以讓我們集中管理整個 App 的色碼。尤其是大型的 App 有上萬行的程式碼時,想改變色碼都不是件簡單的事情。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Alex Alvarez on Unsplash
Read More

Dispatch Queue 教學

GCD 提供有效率的並行處理,讓我們不需要直接管理多執行緒。它的 Dispatch Queues 可以循序地(serially)或是並行地(concurrently)執行任務。我們只需要將要並行的程式當作任務提交到 dispatch queues 就可以了。
Read More
Photo by Florinel Gorgan on Unsplash
Read More

如何製作一個 XCFramework

XCFramework 讓你可以將 iPhone、iPhone 模擬器等多的不同平台的二進位碼打包到一個可發佈的 .xcframework 檔。你只需要為你的 Framework 產生出一個 .xcframework 檔,就可以支援多種平台。
Read More