前言
本文探討了如何使用 onScrollGeometryChange
檢視修飾符有效地監控和管理滾動位置和幾何。透過詳細的程式碼示例和解釋,你將學習如何利用這些工具建立動態和響應迅速的用戶界面。
SwiftUI 是一個強大的框架,它簡化了在蘋果平臺上構建用戶界面的過程。SwiftUI 中的一個基本元件是 ScrollView,它允許使用者透過滾動導航內容。然而,管理滾動位置和理解滾動互動可能是一個挑戰。ScrollGeometry 和 onScrollGeometryChange 檢視修飾符的引入解決了這些挑戰,為開發者提供了更多的控制和對滾動行為的深入瞭解。
什麼是 ScrollPosition
ScrollPosition 是一種型別,允許開發者以程式設計方式讀取或更改滾動位置。雖然有用,但當用戶使用手勢與滾動檢視互動時,它顯得不夠全面。以下是一個展示 ScrollPosition 使用的示例:
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) } }
在這個示例中,我們將滾動檢視繫結到一個狀態屬性。當按下按鈕時,滾動檢視會將其內容偏移移動到指定點。然而,我們無法讀取使用者透過手勢互動設定的具體內容偏移。
引入 ScrollGeometry
SwiftUI 的新 ScrollGeometry 型別以及 onScrollGeometryChange 檢視修飾符提供了一個解決方案。這些工具允許開發者在使用者互動期間準確讀取內容偏移。
使用 onScrollGeometryChange
讓我們探索如何使用 onScrollGeometryChange 檢視修飾符與 ScrollGeometry:
struct ContentView: View { @State private var scrollPosition = ScrollPosition(y: 0) @State private var offsetY: CGFloat = 0 var body: some View { ScrollView { ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) .id(number) } } .scrollPosition($scrollPosition) .onScrollGeometryChange(for: CGFloat.self) { geometry in geometry.contentOffset.y } action: { oldValue, newValue in if oldValue != newValue { offsetY = newValue } } .onChange(of: offsetY) { print(offsetY) } } }
onScrollGeometryChange 檢視修飾符接受三個引數:
型別引數:指定要跟蹤的滾動幾何型別。在此示例中,我們使用
CGFloat
來跟蹤內容偏移的 Y 軸。轉換閉包:從 ScrollGeometry 例項中提取所需資訊。
動作閉包:處理滾動幾何的變化,透過比較舊值和新值,允許我們相應地更新狀態屬性。
高階滾動幾何跟蹤
ScrollGeometry 提供了許多有價值的屬性,如內容偏移、邊界、容器大小、可見矩形、內容插入和內容大小。開發者可以提取單個屬性或組合多個屬性以獲得全面的見解。
以下是一個結合內容大小和可見矩形跟蹤的示例:
struct ContentView: View { struct ScrollData: Equatable { let size: CGSize let visible: CGRect } @State private var scrollPosition = ScrollPosition(y: 0) @State private var scrollData = ScrollData(size: .zero, visible: .zero) var body: some View { ScrollView { ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) .id(number) } } .scrollPosition($scrollPosition) .onScrollGeometryChange(for: ScrollData.self) { geometry in ScrollData(size: geometry.contentSize, visible: geometry.visibleRect) } action: { oldValue, newValue in if oldValue != newValue { scrollData = newValue } } .onChange(of: scrollData) { print(scrollData) } } }
在這個示例中,我們定義了一個 ScrollData
結構來儲存大小和可見矩形屬性。在使用 onScrollGeometryChange 檢視修飾符時,我們將 ScrollData
作為轉換閉包的返回型別,從 ScrollGeometry 例項中提取所有所需的資料。
完整程式碼示例分析
下面是一個完整的 SwiftUI Demo,其中包含了我們剛剛討論的 ScrollView、ScrollGeometry 和 onScrollGeometryChange 的使用示例。你可以在 Xcode 中執行這個專案來觀察其效果。
完整程式碼示例
import SwiftUI struct ContentView: View { @State private var scrollPosition = ScrollPosition(y: 0) @State private var offsetY: CGFloat = 0 var body: some View { VStack { Text("Scroll Offset: \(offsetY, specifier: "%.2f")") .padding() ScrollView { ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) .padding() .frame(maxWidth: .infinity) .background(Color(.secondarySystemBackground)) .cornerRadius(8) .padding(.horizontal) .id(number) } } .scrollPosition($scrollPosition) .onScrollGeometryChange(for: CGFloat.self) { geometry in geometry.contentOffset.y } action: { oldValue, newValue in if oldValue != newValue { offsetY = newValue } } .onChange(of: offsetY) { print(offsetY) } } } } struct ScrollData: Equatable { let size: CGSize let visible: CGRect } struct AdvancedContentView: View { @State private var scrollPosition = ScrollPosition(y: 0) @State private var scrollData = ScrollData(size: .zero, visible: .zero) var body: some View { VStack { Text("Content Size: \(scrollData.size.width, specifier: "%.2f") x \(scrollData.size.height, specifier: "%.2f")") .padding() Text("Visible Rect: \(scrollData.visible.origin.x, specifier: "%.2f"), \(scrollData.visible.origin.y, specifier: "%.2f") - \(scrollData.visible.width, specifier: "%.2f") x \(scrollData.visible.height, specifier: "%.2f")") .padding() ScrollView { ForEach(1..<100, id: \.self) { number in Text(verbatim: number.formatted()) .padding() .frame(maxWidth: .infinity) .background(Color(.secondarySystemBackground)) .cornerRadius(8) .padding(.horizontal) .id(number) } } .scrollPosition($scrollPosition) .onScrollGeometryChange(for: ScrollData.self) { geometry in ScrollData(size: geometry.contentSize, visible: geometry.visibleRect) } action: { oldValue, newValue in if oldValue != newValue { scrollData = newValue } } .onChange(of: scrollData) { print(scrollData) } } } } @main struct ScrollViewDemoApp: App { var body: some Scene { WindowGroup { TabView { ContentView() .tabItem { Label("Basic", systemImage: "1.square.fill") } AdvancedContentView() .tabItem { Label("Advanced", systemImage: "2.square.fill") } } } } }
如何執行
開啟 Xcode 並建立一個新的 SwiftUI 專案。
將預設生成的
ContentView.swift
檔案替換為上面的完整程式碼。在
@main
註釋下的應用程式入口點中,確保你的主檢視是ScrollViewDemoApp
。執行專案。
功能解釋
ContentView
: 展示基本的滾動偏移追蹤功能,透過onScrollGeometryChange
檢視修飾符追蹤 Y 軸的內容偏移。AdvancedContentView
: 展示更高階的滾動幾何追蹤功能,追蹤內容大小和可見矩形的變化。ScrollViewDemoApp
: 包含TabView
,方便在基本和高階示例之間切換。
總結
今天,我們探討了 SwiftUI 中的新 ScrollGeometry 型別和 onScrollGeometryChange 檢視修飾符。這些工具為開發者提供了對滾動位置和互動的精確控制和洞察,增強了動態和響應迅速的用戶界面的開發。透過利用這些功能,你可以建立更具吸引力和直觀的應用程式。