建立第一個 SwiftUI App:Day 4 – List, HStack, Text, Spacer

Photo by Meelika Marzzarella on Unsplash
Photo by Meelika Marzzarella on Unsplash
本章我們會為 Coffee Shop App 加上商品列表。在這頁面中,你會學習到如何使用 SwiftUI 中的 List、HStack、Text 和 Spacer 等 UI 元件。此外,還會介紹 ForEach 和 Identity Key Path (\.field)。

本章我們會為 Coffee Shop App 加上商品列表。在這頁面中,你會學習到如何使用 SwiftUI 中的 ListHStackTextSpacer 等 UI 元件。此外,還會介紹 ForEach 和 Identity Key Path (\.field)。還沒有接觸過 SwiftUI 的讀者,本章可能會有點吃力。請多花一點時間消化一下內容。

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

新增商品資料 – Item

在 CoffeeShop 資料夾下面新增一個 Product 資料夾。之後有關商品的程式碼都會放在這個資料夾下面。

在 Product 下新增 Item.swift。Item 代表一個商品,商品有名稱 (name) 和 價格 (price)。

// Item.swift
struct Item {
    let name: String?
    let price: Double
}

你在 Coffee Shop 完整的程式碼中是找不到 Item。因為我們之後會用 Product 來取代它。之所以目前會取名為 Item,是因為怕之後會搞混。

新增商品列 – Row

在 Product 資料夾下新增 ProductRowView.swift。請直接選擇 SwiftUI View 範本來建立。

新增 SwiftUI 檔案
新增 SwiftUI 檔案

我們想要在列表中的每一列上顯示一個商品。而每一列的最左邊顯示商品名稱,最右邊顯示商品價格。最後商品列就會看起來如下圖:

商品列
商品列

HStack 是一個容器 (Container)。它可以自動將裡面的 Child Views 做水平排列。HStack 的 H 指的就是水平 (Horizontal)。相對應的,VStack 就會做垂直排列。

Text 可用來顯示文字。所以我們需要兩個來顯示名稱和價格。

HStack 會自動將 Child Views 依序向左靠齊排列。可是我們希望名稱是靠左,而價格是靠右。所以我們這兩個中間插入 SpacerSpacer 的寬度會自動撐到最大。所以就可以將名稱和價格擠到兩邊。

最後 ProductRowView 的程式碼就會如下。它會接收一個 Item,並將 Item 顯示出來。其中注意到最後一行程式碼,我們將 HStack 的寬度撐到無限大,也就是 Row 本身的寬度會是無限大。到時候放到 List 裡面,就會撐滿 List 的寬度。

struct ProductRowView: View {
    let product: Item

    var body: some View {
        HStack {
            Text(product.name ?? "")
                .font(.custom("Georgia", size: 20))  // 將字體設為 Georgia, 大小為 20
            Spacer()
            Text(String(format: "$ %.2f", product.price))  //  價格顯示至小數後兩位
                .font(.custom("Georgia", size: 20))
        }
            .frame(height: 44)  // HStack 的高度設為 44
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)  // HStack 的寬度為無限大
    }
}

在 ProductRowView.swift 最下方還可以看到 ProductRowView_Previews。將它替換成下方的程式碼。

struct ProductRowView_Previews: PreviewProvider {
    static var previews: some View {
        ProductRowView(product: Item(name: "Americano", price: 25))
    }
}

然後點擊右上方的 Resume。

按下 Resume
按下 Resume

你就可以看到 ProductRowView_Previews 的預覽。也就是說,我們可以藉由 ProductRowView_Previews 來預覽 ProductRowView 目前是長怎麼樣。我非常喜歡這個功能!

預覽商品列
預覽商品列

新增商品列表 – List

在 Product 資料夾下新增 ProductListView.swift。

我們會將每一個商品顯示在列表上。因為我們還沒有新增商品的功能,所以先在程式碼裡定義兩個商品。

對於每一個 Child View,我們要提供一個唯一識別碼給 List。我們用 Item.name 作為唯一識別碼。程式碼中的斜線 (\) 是 Identity Key Path,這是 Swift 新的語法。所以 \.name 就是告訴 List 使用 Item.name 作為 id。

ForEach 會輪詢 products 陣列裡面的每個 Item。然後,我們在 closure 裡面對每個 Item 回傳 ProductRowView。之後 List 會依據 ForEach 來畫出每一列。

struct ProductListView: View {
    let products = [
        Item(name: "Americano", price: 2.5),
        Item(name: "Espresso", price: 3),
    ]
    
    var body: some View {
        List {
            ForEach(products, id: \.name) { item in
                ProductRowView(product: item)
            }
        }
    }
}

最後預覽一下我們剛剛的商品列表程式。

預覽商品列表
預覽商品列表

另外,List 也可以不需要 ForEach,可以直接對 products 陣列做輪詢。

struct ProductListView: View {
    let products = [
        Item(name: "Americano", price: 2.5),
        Item(name: "Espresso", price: 3),
    ]
    
    var body: some View {
        List(products, id: \.name) { item in
            ProductRowView(product: item)
        }
    }
}

整合

商品列表 ProductListView 和商品列 ProductRowView 都開發完成。但是我們剛剛都只是藉由預覽來確認頁面是否設計正確。我們都還沒有執行過程式啊!

在 CoffeeShop 資料夾下的 ContentView,是專案建立時就一起創建好的。在另外一個檔案 SceneDelegate 裡,會預設用 ContentView 來做第一個頁面。有興趣的話可以看一下 。

ContentView 裡面放入 ProductListView。終於大功告成!跑一下程式看看結果吧!

struct ContentView: View {
    var body: some View {
        ProductListView()
    }
}

結語

本章我們終於踏入 SwiftUI 程式設計。文中介紹了 4 種 SwiftUI 元件,分別是 List、HStack、Text 和 Spacer。還有 SwiftUI 的預覽功能,以及 Swift 的 Identity Key Path。如果你有開發過 UIKit 的經驗的話,會感覺到 SwiftUI 和 UIKit 是截然不同的開發方式。此外,Xcode 提供的預覽功能非常地方便。讓你不需要先把程式都寫好,才能看你的畫面有沒有畫正確。而是可以直接預覽你目前正在開發的 View。

發佈留言

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

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
Photo by Fabian Gieske on Unsplash
Read More

SwiftUI @State & @Binding 教學

SwiftUI 推出了兩個 Property Wrapper – @State and @Binding。利用它們可以達到變數的 Two-way Binding 功能。也就是當變數的值改變時,它會重新被顯示。本章藉由製作一個 Custom View 來展示如何使用 @State 和 @Binding。
Read More
Photo by Svitlana on Unsplash
Read More

iOS:禁止螢幕截圖

基於一些理由,我們可能會想要禁止使用者對我們的 app 做螢幕截圖。然而,iOS 並沒有提供這樣的功能。所幸,我們可以利用 UITextField 來達到此效果。
Read More