如何使用 SwiftUI 中 ScrollView 的滾動偏移
前言
WWDC 24 已經(jīng)結(jié)束,我決定開始寫一些關于 SwiftUI 框架即將推出的新特性的文章。今年,蘋果繼續(xù)填補空白,引入了對滾動位置更細粒度的控制。本周,我們將學習如何操作和讀取滾動偏移。
使用 scrollPosition
SwiftUI 框架已經(jīng)允許我們通過視圖標識符跟蹤和設置滾動視圖的位置。這種方法效果不錯,但不足以更準確地跟蹤用戶交互。
struct ContentView: View {
@State private var position: Int?
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $position)
}
}
在上面的代碼示例中,我們使用了視圖標識符和 scrollPosition 修飾符來跟蹤和設置滾動視圖的位置。雖然這種方法效果不錯,但在某些情況下,尤其是需要更精確的用戶交互跟蹤時,它可能不夠用。為了彌補這一不足,SwiftUI 引入了新的 ScrollPosition 類型,使我們能夠通過偏移量、滾動視圖的邊緣、視圖標識符等組合滾動位置。
新的 ScrollPosition 類型
SwiftUI 框架引入了新的 ScrollPosition 類型,使我們能夠通過偏移量、滾動視圖的邊緣、視圖標識符等組合滾動位置。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to bottom") {
position.scrollTo(edge: .bottom)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
Button("Scroll to top") {
position.scrollTo(edge: .top)
}
}
.scrollPosition($position)
}
}
如上例所示,我們定義了 position 狀態(tài)屬性,并使用 scrollPosition 視圖修飾符將滾動視圖與狀態(tài)屬性綁定。我們還放置了兩個按鈕,允許你快速滾動到滾動視圖中的第一個或最后一個項目。ScrollPosition 類型提供了許多重載的 scrollTo 函數(shù),使我們能夠處理不同的情況。
為滾動添加動畫
通過附加動畫視圖修飾符并傳遞 ScrollPosition 類型的實例作為 value 參數(shù),我們可以輕松地為編程滾動添加動畫。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to bottom") {
position.scrollTo(edge: .bottom)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
Button("Scroll to top") {
position.scrollTo(edge: .top)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
滾動到特定項目
我們添加了另一個按鈕來將滾動視圖的位置更改為隨機項目。我們?nèi)匀皇褂?ScrollPosition 類型的 scrollTo 函數(shù),但我們提供了一個可哈希的標識符。這個選項允許我們將位置更改為特定項目,通過使用 anchor 參數(shù),我們可以選擇所選視圖的哪個點應該可見。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll somewhere") {
let id = (1..<100).randomElement() ?? 0
position.scrollTo(id: id, anchor: .center)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
滾動到特定偏移
最后但同樣重要的是 scrollTo 函數(shù)的 point 參數(shù)重載,允許我們傳遞 CGPoint 實例以將視圖滾動到內(nèi)容的特定點。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to offset") {
position.scrollTo(point: CGPoint(x: 0, y: 100))
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
如上例所示,我們使用帶有 CGPoint 參數(shù)的 scrollTo 函數(shù)。它還提供重載,允許我們僅按 X 或 Y 軸滾動視圖。
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
var body: some View {
ScrollView {
Button("Scroll to offset") {
position.scrollTo(y: 100)
position.scrollTo(x: 200)
}
ForEach(1..<100) { index in
Text(verbatim: index.formatted())
.id(index)
}
}
.scrollPosition($position)
.animation(.default, value: position)
}
}
讀取滾動位置
我們學習了如何使用新的 ScrollPosition 類型操作滾動位置,這也允許我們讀取滾動視圖的位置。ScrollPosition 提供了可選的 edge、point 和 viewID 屬性,以在你編程滾動時讀取值。
每當用戶與滾動視圖交互時,這些屬性將變?yōu)?nil。ScrollPosition 類型上的 isPositionedByUser 屬性允許我們了解何時用戶手勢移動滾動視圖內(nèi)容。
提供一個可以運行示例:
下面是一個可以運行的示例代碼,演示如何讀取和顯示滾動視圖的位置。我們將使用一個 Text 視圖來顯示當前滾動位置:
import SwiftUI
struct ContentView: View {
@State private var position = ScrollPosition(edge: .top)
@State private var scrollOffset: CGPoint?
var body: some View {
VStack {
ScrollView {
LazyVStack {
ForEach(0..<100) { index in
Text("Item \(index)")
.id(index)
.padding()
.background(Color.yellow)
.cornerRadius(10)
.padding(.horizontal)
}
}
.scrollPosition($position)
.onScrollGeometryChange { geometry in
scrollOffset = geometry?.contentBounds.origin
}
}
.animation(.default, value: position)
if let offset = scrollOffset {
Text("Scroll Offset: x = \(Int(offset.x)), y = \(Int(offset.y))")
.padding()
} else {
Text("Scroll Offset: not available")
.padding()
}
}
.padding()
}
}
在這個示例中,我們使用了 onScrollGeometryChange 修飾符來讀取滾動視圖的幾何變化。每當滾動視圖滾動時,geometry?.contentBounds.origin 將提供當前滾動位置的偏移量。我們將這個偏移量存儲在 scrollOffset 狀態(tài)屬性中,并在視圖底部顯示當前的滾動位置。
總結(jié)
在本文中,我們深入探討了 SwiftUI 框架中 ScrollView 的新特性,特別是如何通過 ScrollPosition 類型實現(xiàn)更精確的滾動控制。我們介紹了如何使用 ScrollPosition 類型進行滾動位置的設置和讀取,包括使用偏移量、視圖標識符等方式進行操作。此外,我們還展示了如何通過動畫和事件處理來增強用戶體驗。通過這些新功能,開發(fā)者可以更靈活地控制滾動視圖的行為,從而創(chuàng)建更加流暢和直觀的用戶界面。希望這些內(nèi)容對你有所幫助。