精通 SwiftUI - iOS 17 版

第 44 章
使用Grid API 創建網格佈局

自 iOS 16 開始,SwiftUI 引入了一個新的 Grid API 來組成基於網格的佈局。 你可以使用 VStackHStack 安排相同的佈局。 然而,Grid 視圖使其變得容易得多。 Grid 視圖是一種容器視圖,它以二維佈局排列其他視圖。

基本用法

讓我們從一個簡單的網格開始。 要創建一個 2x2 網格,你可以編寫如下程式碼:

struct ContentView: View {
    var body: some View {
        Grid {
            GridRow {
                Color.purple
                Color.orange
            }

            GridRow {
                Color.green
                Color.yellow
            }
        }
    }
}

假設你已經在 Xcode 中創建了一個 SwiftUI 項目,你應該會看到一個 2x2 的網格,其中填上了不同的顏色。

圖 44.1. 一個簡單的網格(Grid)
圖 44.1. 一個簡單的網格(Grid)

要創建 3x3 網格,你只需添加另一個 GridRow。 並且,對於每個GridRow,再插入一個子視圖。

圖 44.2. 一個 3x3 的網格
圖 44.2. 一個 3x3 的網格

比較網格視圖和堆棧視圖

你可能想知道為什麼我們需要使用 Grid API。 我們其實可以使用 HStackVStack創建相同的網格 UI。 那麼,蘋果為什麼要引入這個新的 API? 對!你可以使用堆棧視圖構建相同的網格。 然而,有一個主要的區別使得 Grid 視圖在構建網格佈局時更受歡迎。

讓我們使用下面的程式碼創建另一個 2x2 網格:

struct ContentView: View {
    var body: some View {
        Grid {
            GridRow {
                Image(systemName: "trash")
                    .font(.system(size: 100))
                Image(systemName: "trash")
                    .font(.system(size: 50))
            }

            GridRow {
                Color.green
                Color.yellow
            }
        }
    }
}

這次,第一行顯示兩個系統圖像,第二行顯示彩色視圖。 您的預覽應顯示如下所示的網格。

圖 44.3. 2x2 網格以顯示圖像和顏色視圖
圖 44.3. 2x2 網格以顯示圖像和顏色視圖

要使用嵌套的VStackHStack構建相同的網格佈局,我們可以編寫如下程式碼:

VStack {
    HStack {
        Image(systemName: "trash")
            .font(.system(size: 100))
            .frame(minWidth: 0, maxWidth: .infinity)
        Image(systemName: "trash")
            .font(.system(size: 50))
            .frame(minWidth: 0, maxWidth: .infinity)
    }

    HStack {
        Color.green
        Color.yellow
    }
}

在預設的情況下,Grid 視圖的每一列在一行中佔據相等的空間。 但對於 HStack,我們必須附加 frame 修飾器以使每張圖像在行中佔據相同的空間。

Grid 視圖只是讓創建網格視圖變得更容易,並提供了幾個修改器來自定義網格佈局。

使用 gridCellColumns 合併單元格

創建網格視圖時,你可能希望在特定行中顯示單個單元格,而其他行繼續顯示多個單元格。 gridCellColumns 修飾器專為你合併單元格而設計。 這是一個例子:

Grid {
    GridRow {
        Image(systemName: "trash")
            .font(.system(size: 100))
        Image(systemName: "trash")
            .font(.system(size: 50))
    }

    GridRow {
        Color.purple
            .overlay {
                Image(systemName: "magazine.fill")
                    .font(.system(size: 100))
                    .foregroundColor(.white)
            }
            .gridCellColumns(2)
    }
}

第二行只有一個單元格。 我們附加 gridCellColumn 修飾器並將其值設置為 2 以合併單元格。 如果你不使用修飾器,就會看到一個空白單元格。

圖 44.4. 合併單元格
圖 44.4. 合併單元格

添加空白單元格

圖 44.5. 3x3 網格視圖
圖 44.5. 3x3 網格視圖

現在讓我們像這樣創建另一個 3x3 網格視圖:

struct ContentView: View {
    var body: some View {
        Grid {
            GridRow {
                IconView(name: "cloud")
                IconView(name: "cloud")
                IconView(name: "cloud")
            }

            GridRow {
                IconView(name: "cloud")
                IconView(name: "cloud")
                IconView(name: "cloud")
            }

            GridRow {
                IconView(name: "cloud")
                IconView(name: "cloud")
                IconView(name: "cloud")
            }
        }
    }
}

struct IconView: View {
    var name: String = "trash"

    var body: some View {
        Image(systemName: name)
            .frame(width: 100, height: 100)
            .background(in: Rectangle())
            .backgroundStyle(.purple)
            .foregroundStyle(.white.shadow(.drop(radius: 1, y: 3.0)))
            .font(.system(size: 50))
    }
}

如果我們想為網格中心的單元格顯示一個空白視圖怎麼辦? 為此,你可以使用以下程式碼添加一個空白單元格:

Color.clear
    .gridCellUnsizedAxes([.horizontal, .vertical])

如果用上面的程式碼替換中心單元格,你將在網格中心看到一個空白單元格。

圖 44.6. 加入空白格
圖 44.6. 加入空白格

gridCellUnsizedAxes 修飾器可防止空白單元格佔用的空間超過行或列中其他單元格所需的空間。 如果省略修飾器,就會出現如圖 7 所示的網格佈局。

圖 44.7. 省略 gridCellUnsizedAxes 修飾器
圖 44.7. 省略 gridCellUnsizedAxes 修飾器

調整單元格間距

要控制單元格之間的垂直和水平間距,你可以在建立 Grid 視圖時使用 horizontalSpacingverticalSpacing 參數:

Grid(horizontalSpacing: 0, verticalSpacing: 0) {
  .
  .
  .
}

如果需要在行之間添加一些間距,可以將 padding 修飾器附加到 GridRow。 圖 8 顯示了一個示例。

圖 44.8. 調整間距
圖 44.8. 調整間距

控制單元對齊

Grid 視圖有一個名為 alignment 的參數,供你配置單元格的預設對齊方式。 例如,此處將對齊方式設置為 .bottom :

Grid(alignment: .bottom, horizontalSpacing: 0, verticalSpacing: 0) {
  .
  .
  .
}

如果你更改了程式碼,黑框應與單元格底部就會對齊。

圖 44.9. 設置對齊方式
圖 44.9. 設置對齊方式

要更改預設對齊設置,單元格本身可以附加 gridCellAnchor 修飾器來更改對齊方式。 圖 44.10 顯示了一個示例。

圖 44.10. 更改預設對齊設置
圖 44.10. 更改預設對齊設置

總結

Grid API 為開發人員提供了構建基於網格的佈局的更多選項。 你可以使用HStackVStack來構建類似的佈局。 但是 Grid 視圖可以為你節省大量代碼並使你的程式碼更具可讀性。 如果你的App只需要支持最新版本的 iOS,可以嘗試使用 Grid API 來構建一些有趣的佈局。

在本章所準備的範例檔中,有最後完整的 Xcode 專案,可供你下載參考: