歡迎閱讀 SwiftUI 小技巧系列教程!在這篇文章中,我們將實作一個常見的手機 UI 設計 —— Card UI。SwiftUI 框架讓我們輕易構建 App UI,我們會在文章中展示這一點。你將會看到我們可以使用堆疊 (stack)、圖像、和文本視圖,來像這樣創建卡片視圖 (card view)。
請注意,本篇教程需要你在 macOS Catalina (v10.15)上運行 Xcode 11。
建立一個卡片式的 UI
如果你還沒有開啟 Xcode,請打開它,並使用 Single View Application 模板來建立一個新專案。在下一個畫面,設定專案名稱為 SwiftUIScrollView
(或你喜歡的名稱),並填入所需要的值。請確認你有在 User Interface 選項中選取 SwiftUI
。
如果你還不熟悉 SwiftUI,你可能會在 ContentView.swift
文件中為 UI 編寫程式碼。這當然沒有問題,但是我想向你展示一個更好的方法來組織程式碼。為了實作卡片視圖,讓我們為它建立一個單獨的文件。在專案導航器中,右鍵單擊SwiftUIScrollView
,並選擇 New File…。
在 User Interface 區塊,選取 SwiftUI View 模板並點擊 Next 來建立檔案。將檔案命名為 CardView
,並儲存在專案資料夾內。
CardView.swift
中的程式碼與 ContentView.swift
的程式碼很接近。同樣地,你可以在畫布中檢視 UI。
準備圖片檔
現在我們準備要寫卡片視圖的程式碼。首先,你需要準備圖檔並將其匯入素材目錄。如果你不想要準備自己的圖片,你可以在這裡下載圖片檔,將圖片檔解壓縮後,選取 Assets.xcassets
,並將其拖曳至素材目錄。
實作卡片視圖
現在切回 CardView.swift
檔。如果你再看一下以下圖片,這個卡片視圖是由兩個部分組成的:上面的部分是圖片,而下面的部分是文字敘述。
讓我們從圖片開始。我將會設計讓圖片可以調整大小,讓它因應畫面縮放,同時保持長寬比例。你可以撰寫程式碼如下:
struct CardView: View {
var body: some View {
Image("swiftui-button")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
如果你忘記了這兩個修飾器是甚麼,可以回去有關 Image
物件的章節閱讀一下。接下來實作文字敘述部分,你可以將程式碼撰寫如下:
VStack(alignment: .leading) {
Text("SwiftUI")
.font(.headline)
.foregroundColor(.secondary)
Text("Drawing a Border with Rounded Corners")
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
Text("Written by Simon Ng".uppercased())
.font(.caption)
.foregroundColor(.secondary)
}
很明顯的,你需要使用 Text
來建立文字視圖。因為我們在敘述中有三種文字視圖,以垂直方式並排,所以我們使用一個 VStack
來嵌入它們。對於 VStack
,我們指定以 .leading
來對齊,這會將文字視圖對齊堆疊視圖的左側。
我們已經在之前的教學文章討論過這些 Text
的修飾器,如果你對任何一個修飾器有所疑問的話,可以回去參考看看。不過這裡要提出來說明的,是 .primary
與 .secondary
顏色。當你在 foregroundColor
修飾器指定標準顏色,像是 .black
與 .purple
,iOS 13 導入一套系統顏色,包括了三原色 (primary colors)、三間色 (secondary colors) 以及再間色 (tertiary colors) 的變化,透過這個顏色變化,你的 App 可以很容易地支援淡色與深色模式,舉例來說,文字視圖的主色預設以淡色模式設為黑色。當 App 切換到深色模式,主色將會被調整成白色。這都會由 iOS 自動來調整,所以你不用再另外寫支援深色模式的程式碼。我們將在後面的章節深入討論深色模式。
要將圖片與這些文字視圖垂直排列,我們使用 VStack
來嵌入它們,目前的佈局如下圖所示。
但我們還沒完成,還有幾件事需要實作。首先,如果敘述區塊要對齊圖片的邊緣,該如何做呢?
依照我們所學,我們可以在一個 HStack
嵌入文字視圖的 VStack
。然後,我們將使用一個彈性空間 (Spacer
) 來將 VStack
往左推。讓我們來看看是否可行。
如果你已經變更程式碼如下,這個文字視圖的 VStack
會對齊其畫面的左側。不過,前端被截掉了。
調整佈局順序
文字堆疊與彈性空間 (spacer) 預設佔據了父視圖的一半,這也是標題無法完整呈現的原因。要解決這個問題,你需要使用 layoutPriority
修飾器來調整文字堆疊的佈局優先度 (layout priority)。數值越大表示優先度越高。這表示如果我們設定文字視圖的 VStack
佈局優先度為較大的值時,iOS 就會在配置空間給彈性空間前,提供更多空間來完全渲染文字視圖。下圖為最後的結果。
如果在 HStack
加上一些間距 (padding) 應該會更好。插入 padding
修飾器如下:
HStack {
VStack(alignment: .leading) {
.
.
.
}
.layoutPriority(100)
Spacer()
}
.padding()
最後是外框部分。在上一篇文章,我們有討論過如何畫出具有圓角的外框。我們可以使用 overlay
修飾器,並使用 RoundedRectangle
來畫出外框。以下是完整的程式碼:
struct CardView: View {
var body: some View {
VStack {
Image("swiftui-button")
.resizable()
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text("SwiftUI")
.font(.headline)
.foregroundColor(.secondary)
Text("Drawing a Border with Rounded Corners")
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
Text("Written by Simon Ng".uppercased())
.font(.caption)
.foregroundColor(.secondary)
}
.layoutPriority(100)
Spacer()
}
.padding()
}
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(.sRGB, red: 150/255, green: 150/255, blue: 150/255, opacity: 0.1), lineWidth: 1)
)
.padding([.top, .horizontal])
}
}
除了外框之外,我也在頂部、和左右兩側分別加入了一些間距。現在,我們已經建立好卡片視圖的佈局了。
讓卡片視圖更彈性
雖然目前卡片視圖目前看起來沒問題,但我們是把圖片與文字用程式碼固定寫入。為了讓它更彈性,我們重構一下程式碼。首先,在 CardView
宣告 image、category、heading 與 author 這些變數:
var image: String
var category: String
var heading: String
var author: String
接下來,將 Image
與 Text
視圖以這些變數替代:
VStack {
Image(image)
.resizable()
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text(category)
.font(.headline)
.foregroundColor(.secondary)
Text(heading)
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
Text(author.uppercased())
.font(.caption)
.foregroundColor(.secondary)
}
.layoutPriority(100)
Spacer()
}
.padding()
}
做完變更之後,你將會在 CardView_Previews
結構中見到一個錯誤。這是因為我們在 CardView
導入了一些變數,所以在使用它時,我們要指定參數。
將程式碼以下面這段替代:
struct CardView_Previews: PreviewProvider {
static var previews: some View {
CardView(image: "swiftui-button", category: "SwiftUI", heading: "Drawing a Border with Rounded Corners", author: "Simon Ng")
}
}
錯誤將會被修正,現在你已經建立了一個能夠接受不同圖片與文字的彈性 CardView
。你可以基於構建好的內容,進一步應用此卡片視圖來構建一個流暢的列表 UI。
你覺得這個 SwiftUI 小技巧教學系列怎樣呢?如果你喜歡本篇文章,覺得它對你有幫助,請留言讓我知道。另外,如果你對我們的下一個技巧有任何建議,也請留言告訴我們。