建立第一個 SwiftUI App:Day 9 – TabView, ZStack, Image

Photo by Jason Briscoe on Unsplash
Photo by Jason Briscoe on Unsplash
本章我們會為 Coffee Shop App 加入菜單的畫面。這畫面中,我們會使用到 TabView, ZStack, Image, 和 GeometryReader 這些新的 UI 元件。另外,還會介紹如何設定 List 的背景顏色,以及如何自訂字體和大小。

本章我們會為 Coffee Shop App 加入菜單的畫面。這畫面中,我們會使用到 TabView, ZStack, Image, 和 GeometryReader 這些新的 UI 元件。另外,還會介紹如何設定 List 的背景顏色,以及如何自訂字體和大小。

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

加入 TabView

新增一個資料夾叫 Menu,然後它下面新增一個 SwiftUI View 叫 MenuView,這會是菜單的主頁面。

回到 ContentView,在裡面加入 TabViewTabView 裡面有 ProductListViewMenuView 兩個頁面。這樣就有 TabBar 出現了。

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                ProductListView()
            }
                .tabItem {
                    Image("ShopIcon")
                    Text("Shop")
                }

            MenuView()
                .tabItem {
                    Image("MenuIcon")
                    Text("Menu")
                }
        }
    }
}

.tabItem 中,我們要設定 Tab 的圖示還有 Tab 的標題。用 Image 就可以顯示圖片。Image 的參數是 Assets.xcassets 中的 Image Set 名稱。下面已經準備好這兩個 Tab 圖示,請先新增到 Assets.xcassets,並分別命名為 ShopIconMenuIcon

然後執行一下程式,看看剛剛加入的 TabView 吧。

SwiftUI TabView
SwiftUI TabView

菜單與背景圖

現在來替 MenuView 加上背景圖,這樣整體會比較像一般紙本的 Menu。下載下面的背景圖片,新增到 Assets.xcassets,並命名為 MenuBackground

MenuView 的 Layout 如下方程式碼。ZStack 相對於 VStack/HStack,它是前後的順序。所以會先是背景圖 MenuBackground,然後前方是商品列表。

我們對背景圖呼叫 .resizable() 讓它可以填滿畫面。這是因為背景圖的尺寸和螢幕不是很合,而我們又不希望會有留白的地方。可以試著將 .resizable() 移除,看看效果會是怎樣。

MenuView 是被 TabView 包起來。所以我們希望 MenuView 的 Heigh 是不包含 TabBar 的。為了要取得不含 TabBar Height 的 Height,就要用 GeometryReader。如果不用 GeometryReader 的話,背景圖會延伸到 TabBar 的後面去。

背景圖的上方和下方有一些圖案,我們希望商品列表不要遮蓋到,所以將列表的高度縮小。

MenuView 的建構子裡,那 4 行是為了要將 List 的背景設為透明,還有隱藏分隔線。UITableView 是 UIKit 的,而 List 是 SwiftUI 的。兩者都是用來顯示列表的,而且 List 的底層是用 UITableView 實作的。目前 List 不支援設定透明的方法,所以只好透過 UITableView 來設定。

struct MenuView: View {
    @FetchRequest(entity: Product.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Product.name, ascending: false)])
    var products: FetchedResults<Product>

    init() {
        UITableView.appearance().backgroundColor = .clear
        UITableViewCell.appearance().backgroundColor = .clear
        UITableView.appearance().separatorStyle = .none
    }

    var body: some View {
        GeometryReader { proxy in
            ZStack {
                Image("MenuBackground")
                    .resizable()

                List {
                    ForEach(self.products, id: \.id) { product in
                        MenuRowView(product: product)
                    }
                }
                    .frame(width: proxy.size.width * 0.75, height: proxy.size.height * 0.65)
            }
                .frame(width: proxy.size.width, height: proxy.size.height, alignment: .topLeading)
        }
    }
}

商品列

在 Menu 資料夾下新增 MenuRowView,並貼上以下的程式碼。整個商品列是一個 Button,所以點擊列就可以點餐,點餐的部分會在下一張講解。

商品列的左邊是商品名稱,右邊是商品價格。用 .font() 設定成 Georgia 字體,以及大小為 24。.foregroundColor() 是設定字體顏色。所新增一個叫 MenuTextColor 的 Color Set,而且顏色設為 #1A0D02。

struct MenuRowView: View {
    let product: Product

    var body: some View {
        Button(action: {
            // Add an action later
        }) {
            HStack {
                Text(product.name ?? "")
                    .font(.custom("Georgia", size: 24))
                    .foregroundColor(Color("MenuTextColor"))
                Spacer()
                Text(String(format: "$ %.2f", product.price))
                    .font(.custom("Georgia", size: 24))
                    .foregroundColor(Color("MenuTextColor"))
            }
        }
            .buttonStyle(PlainButtonStyle())
            .frame(height: 36)
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
    }
}

執行一下程式,我們已經完成菜單的畫面。

菜單
菜單

結語

本章中使用了 UITableView 來設定列表的背景顏色。這表示 SwiftUI 的功能還不是那麼地完整,畢竟它還很新。所以對 UIKit 還是要有一些了解,偶爾還是需要使用到它。

發佈留言

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

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