精通 SwiftUI - iOS 17 版

第 49 章
使用 SwiftUI 偵測 ScrollView 中的捲動位置

在 SwiftUI 中使用捲動視圖時,其中一個常見問題是如何偵測捲動位置。 在 iOS 17 發布之前,開發人員必須拿出自己的解決方案來偵測滾動位置。 然而,新版本的 SwiftUI 更新了 ScrollView,新增了一個名為 scrollPosition 的新修飾器。 借助這項新功能,開發人員可以輕鬆識別正在滾動到的項目。

在本章中,我們將仔細研究這個新修飾器,並研究它如何幫助你偵測滾動視圖中的滾動位置。

使用 ScrollPosition 修飾符

讓我們從一個顯示 50 個項目的清單的簡單滾動視圖開始。

struct ColorListView: View {
    let bgColors: [Color] = [ .yellow, .blue, .orange, .indigo, .green ]

    var body: some View {
        ScrollView {
            LazyVStack(spacing: 10) {
                ForEach(0...50, id: \.self) { index in

                    bgColors[index % 5]
                        .frame(height: 100)
                        .overlay {
                            Text("\(index)")
                                .foregroundStyle(.white)
                                .font(.system(.title, weight: .bold))
                        }
                }
            }
        }
        .contentMargins(.horizontal, 10.0, for: .scrollContent)

    }
}

如果你熟悉在 SwiftUI 中實作捲動視圖,那麼以上的程式碼對你應該非常簡單。 我們利用「ForEach」循環來呈現 50 個顏色項,然後將其嵌入到垂直捲動視圖中。 如果你將此程式碼新增至 SwiftUI 專案中,就應該能夠預覽它並看到類似於圖 1 的內容。

圖 49.1. 捲動視圖
圖 49.1. 捲動視圖

要追蹤當前滾動位置或項目,你要先聲明一個狀態變數來保存該位置:

@State private var scrollID: Int?

接下來,將 scrollPosition 修飾器附加到捲動視圖。 此修飾器需要綁定到 scrollID,它儲存滾動位置:

ScrollView {

  ...

}
.scrollPosition(id: $scrollID)

當滾動視圖滾動時,綁定將使用顏色視圖的索引進行更新。最後,將 scrollTargetLayout 修飾器附加到 LazyVStack 視圖,如下所示:

LazyVStack(spacing: 10) {

    ...

}
.scrollTargetLayout()

如果沒有 scrollTargetLayout() 修飾器,scrollPosition 修飾器將無法正常運作。 scrollPosition 修飾器依賴 scrollTargetLayout() 修飾器來配置包含滾動目標的佈局。

要觀察滾動位置的變化,可以將 onChange 修飾器附加到滾動視圖並將滾動 ID 列印到控制台:

.onChange(of: scrollID) { oldValue, newValue in
    print(newValue ?? "")
}

當你捲動清單時,目前捲動位置應顯示在控制台中。

圖 49.2. 偵測捲動位置
圖 49.2. 偵測捲動位置

滾動到頂部

讓我們使用 scrollPosition 修飾器再實作一項功能。 當使用者點擊任何顏色視圖時,清單應會自動捲回頂部。

圖 49.3. 自動捲回頂部功能示範
圖 49.3. 自動捲回頂部功能示範

這可以透過在每個顏色視圖中新增一個「onTapGesture」修飾器,並傳遞一個將其中的「scrollID」設為「0」的閉包來實現。 當使用者點擊任何顏色視圖時,scrollID 將更新為 0,這就會將清單滾動回頂部。

bgColors[index % 5]
    .frame(height: 100)
    .overlay {
        Text("\(index)")
            .foregroundStyle(.white)
            .font(.system(.title, weight: .bold))
    }
    .onTapGesture {
        withAnimation {
            scrollID = 0
        }
    }

調整捲動視圖的內容邊距

現在你知道如何偵測滾動位置,讓我們討論 iOS 17 中滾動視圖的另一個新功能。首先,它是「contentMargins」修飾器。 這是 SwiftUI 中的一項新功能,可讓開發人員自訂可捲動視圖的邊距。 使用contentMargins,你可以輕鬆調整內容與捲動視圖邊緣之間的空間量,從而更好地控制應用程式的佈局。

我們已經在範例程式碼中使用了此修飾器,將水平邊距設為 10 點。

.contentMargins(.horizontal, 10.0, for: .scrollContent)

.scrollContent 參數值指示內容邊距應僅套用於捲動內容,而不是整個捲動視圖。 如果你不指定邊緣和放置參數,則「contentMargins」修飾器會將邊距套用至捲動視圖的所有邊緣。 例如,在下面的程式碼中,它將整個滾動視圖插入 50 點,包括捲軸。

圖 49.4. 設定內容邊距
圖 49.4. 設定內容邊距

如果你想讓捲軸保持在原來的位置,你可以將placement參數設定為.scrollContent

圖 49.5. 更改滾動條的位置
圖 49.5. 更改滾動條的位置

總結

新的 scrollPosition 修飾器是捲動視圖最受期待的功能之一。 在 iOS 17 中,SwiftUI 終於引入了此功能,讓開發人員可以偵測滾動位置。 在本教程中,我們示範了這個新修飾器的用法,並引入了另一個名為contentMargins的新修飾器,它使開發人員能夠自訂可捲動視圖的邊距。

借助 SwiftUI 中的這些新功能,開發人員現在可以輕鬆為其應用程式創建更具客製化和視覺吸引力的佈局。

你可以在此處下載演示項目作為參考: