SwiftUI 最初的版本沒有原生集合視圖 (collection view)。你可以自己構建一個解決方案,或是使用第三方程式庫。在今年的 WWDC 中,Apple 為 SwiftUI 框架引入了許多新功能,其中一個就是實作 Grid 視圖。SwiftUI 現在為開發者提供兩個新的 UI 組件: LazyVGrid 和 LazyHGrid,一個用於創建垂直 Grid,另一種用於創建水平 Grid。正如 Apple 所說,“Lazy” 一詞意思是 Grid 視圖在需要時才會創建項目。
在這篇教學中,我會教你如何創建水平和垂直視圖。LazyVGrid 和 LazyHGrid 都設計得十分靈活,因此開發者可以輕鬆創建各種類型的 Grid 佈局。我們還會深入了解如何更改 Grid 項目的大小,以實現不同的佈局。
SwiftUI Grid 佈局的基礎
我們可以依照以下步驟,創建水平或垂直的 Grid 佈局:
- 準備要顯示在 Grid 中的原始數據。例如,以下是我們將在範例 App 中顯示的一組 SF Symbol:
private var symbols = ["keyboard", "hifispeaker.fill", "printer.fill", "tv.fill", "desktopcomputer", "headphones", "tv.music.note", "mic", "plus.bubble", "video"]
- 創建一個
GridItem
陣列,來描述 Grid 的外觀。比如說,Grid 需要有多少列?以下是描述一個 3 列 (column) Grid 的範例程式碼:
private var threeColumnGrid = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
- 使用
LazyVGrid
和ScrollView
佈局 Grid。可以參考以下這是範例程式碼:
ScrollView {
LazyVGrid(columns: threeColumnGrid) {
// Display the item
}
}
- 如果你想要構建水平 Grid,就如此使用
LazyHGrid
:
ScrollView(.horizontal) {
LazyHGrid(rows: threeColumnGrid) {
// Display the item
}
}
使用 LazyVGrid 來創建垂直 Grid
對 Grid 佈局有一些基本了解之後,讓我們開始編寫程式碼吧!安裝了 Xcode 12 beta 後,使用新的 App 模板創建一個新專案。
在 ContentView.swift
內,宣告以下變數 (variable):
private var symbols = ["keyboard", "hifispeaker.fill", "printer.fill", "tv.fill", "desktopcomputer", "headphones", "tv.music.note", "mic", "plus.bubble", "video"]
private var colors: [Color] = [.yellow, .purple, .green]
private var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
我們將要在一個 3 列 Grid中顯示一組 SF Symbol。如此更新 body
變數來顯示 Grid:
var body: some View {
ScrollView {
LazyVGrid(columns: gridItemLayout, spacing: 20) {
ForEach((0...9999), id: \.self) {
Image(systemName: symbols[$0 % symbols.count])
.font(.system(size: 30))
.frame(width: 50, height: 50)
.background(colors[$0 % colors.count])
.cornerRadius(10)
}
}
}
}
我們使用 LazyVGrid
,並告訴垂直 Grid 使用 3 列佈局。我們還指定行與行之間有 20 point 的間距。在程式碼中,我們有一個 ForEach
迴圈 (loop),來顯示總共 10,000 個圖像視圖。如果有正確地跟隨步驟,你就會在預覽中看到一個三列 Grid。
我們成功創建了一個三列的垂直 Grid 了!現在,圖像框架 (frame) 的大小固定為 50 x 50 point。你可以如此更改框架修飾符 (modifier):
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 50)
然後,圖像的寬度就會擴大到列的寬度。
利用 GridItem 更改 Grid 佈局 (Flexible/ Fixed/ Adaptive)
讓我們進一步看看 GridItem
。你可以使用 GridItem
實例在 LazyHGrid
和 LazyVGrid
視圖中配置項目的佈局。在前文中,我們定義了一個有三個 GridItem
實例的陣列,而每個實例都使用尺寸型別 (Size Type) .flexible()
。Flexible 尺寸型別讓我們可以創建三個大小相等的列。如果想要一個 6 列的 Grid,則可以如此創建 GridItem
陣列:
private var sixColumnGrid: [GridItem] = Array(repeating: .init(.flexible()), count: 6)
.flexible()
只是其中一個用於控制 Grid 佈局的尺寸型別。如果要在一行中放置盡可能多的項目,你可以使用如下的 adaptive 尺寸型別:
private var gridItemLayout = [GridItem(.adaptive(minimum: 50))]
Adaptive 尺寸型別要求你指定項目的最小尺寸 (minimum size)。在上面的程式碼中,我們設定了每個 Grid 項目的最小尺寸為 50。如果你像這樣修改 gridItemLayout
變數,就可以達到以下的 Grid 佈局:
我們使用了 .adaptive(minimum: 50)
,指示 LazyVGrid
在一行中填滿盡可能多的圖像,而每個項目的最小尺寸為 50 point。
除了 .flexible
和 .adaptive
之外,如果想創建固定寬度的列,我們可以使用 .fixed
。例如,我們想將圖像分為兩列,第一列的寬度為 100 point,第二列的寬度為 150 point,我們可以這樣編寫程式碼:
private var gridItemLayout = [GridItem(.fixed(100)), GridItem(.fixed(150))]
同樣地,如果你像這樣更新 gridItemLayout
變數,就會出現尺寸不同的兩列 Grid。
你可以混合使用不同的尺寸型別,來創建更複雜的 Grid 佈局。例如,您可以定義一個 .fixed
的GridItem
,然後定義一個 .adaptive
的GridItem,像這樣:
private var gridItemLayout = [GridItem(.fixed(150)), GridItem(.adaptive(minimum: 50))]
在這個情況下,LazyVGrid
將創建一個固定尺寸的列,其寬度為 100 point。然後,它會讓盡可能多的項目填滿剩餘空間。
使用 LazyHGrid 來創建水平 Grid
現在,您已經創建了垂直 Grid,LazyHGrid
可以很容易地將垂直 Grid 轉換為水平 Grid。水平 Grid 的用法與 LazyVGrid
幾乎完全相同,只是我們會將其嵌入到水平滾動視圖中。此外,LazyHGrid
的參數是 rows
而不是 columns
。
因此,我們只需要重寫幾行程式碼,就可以將 Grid 視圖從垂直方向轉換為水平方向:
ScrollView(.horizontal) {
LazyHGrid(rows: gridItemLayout, spacing: 20) {
ForEach((0...9999), id: \.self) {
Image(systemName: symbols[$0 % symbols.count])
.font(.system(size: 30))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 50, maxHeight: .infinity)
.background(colors[$0 % colors.count])
.cornerRadius(10)
}
}
}
在預覽中執行範例或在模擬器上進行測試,你應該會看到一個水平 Grid。最棒的是,此 Grid 視圖可以自動支援 iPhone 和 iPad。
總結
SwiftUI 第一個版本中缺少的集合視圖現在已經推出了。SwiftUI 中的 LazyVGrid
和 LazyHGrid
讓開發者只需要幾行程式碼,就可以創建不同類型的 Grid 佈局。這篇教學只是簡單介紹了這兩個新 UI 組件,希望大家試試 GridItem
的不同配置,看看你可以實作出怎麼樣的 Grid 佈局吧!
原文:Building Collection Views in SwiftUI with LazyVGrid and LazyHGrid