A SwiftUI & Combine powered scroll-offset sampling component that lets you monitor ScrollView
offsets smoothly and efficiently by sampling at intervals and filtering out insignificant changes.
- Timed Sampling Sample the latest offset at regular intervals (default 0.25s), avoiding excessive updates every frame.
- Minimum-Difference Filtering Only trigger updates when offset changes exceed a threshold (default 2.0pt), cutting down jitter noise.
- Background ↔ Main Dispatch Perform sampling on a background queue and publish on the main thread for safe, smooth UI updates.
- Configurable
Customize sampling interval (
sampleInterval
) and minimum-difference threshold (minDifference
). - Axes & Indicators Monitor horizontal, vertical, or both axes, and choose whether to show scroll indicators.
-
In Xcode, select File → Swift Packages → Add Package Dependency...
-
Enter the repository URL, for example:
https://github.com/asyncawaited/ScrollOffsetReader.git
-
Specify a version range (e.g.
from: "1.0.0"
).
import SwiftUI
import ScrollOffsetReader
struct ContentView: View {
@State private var offset: CGSize = .zero
var body: some View {
VStack {
Text("Offset: x=\(offset.width, specifier: "%.1f"), y=\(offset.height, specifier: "%.1f")")
.padding()
ScrollOffsetReader(
scrollOffset: $offset,
axes: .vertical,
showsIndicators: false,
sampleInterval: 0.2, // Default 0.25
minDifference: 1.0 // Default 2.0
) {
LazyVStack {
ForEach(0..<100) { i in
Text("Item \(i)")
.frame(height: 50)
}
}
}
}
}
}
Parameter | Type | Default | Description |
---|---|---|---|
scrollOffset |
Binding<CGSize> |
— | Binding to receive the sampled scroll offset |
axes |
Axis.Set |
.vertical |
Which axis (or axes) to monitor: .horizontal , .vertical , or both |
showsIndicators |
Bool |
true |
Whether to show scroll indicators |
sampleInterval |
TimeInterval |
0.25 |
Sampling interval in seconds |
minDifference |
CGFloat |
2.0 |
Minimum offset change (in points) required to trigger update |
-
OffsetSampler
- Uses a
CurrentValueSubject<CGSize, Never>
to accept raw offsets. - Applies a custom
sample(with:)
extension to combine offsets with a timer, taking only the latest value when the timer fires. - Removes duplicate values and publishes on the main thread via
@Published var sampledOffset
.
- Uses a
-
ScrollOffsetModifier
- Holds
OffsetSampler
as a@StateObject
. - Captures per-frame offsets via a
PreferenceKey
and forwards them tosampler.update(offset:)
. - Listens to
sampler.$sampledOffset
and writes sampled values back to the boundscrollOffset
.
- Holds
-
ScrollOffsetReader
- Wraps a
ScrollView
,ZStack
,ScrollOffsetProxy
, andScrollOffsetModifier
into one reusable component for one-line integration.
- Wraps a
- Swift: 5.9+
- iOS: 13.0+
- macOS: 11.0+
- Xcode: 15.0+
MIT © Royal
基于 SwiftUI & Combine 的滚动偏移采样组件,帮助你在 ScrollView
中高效、平滑地监听滚动位置,并自动过滤无意义的小幅度抖动。
- 定时采样 每隔指定时间间隔(默认 0.25s)采样最新偏移,避免在每帧都更新导致的性能问题。
- 最小差值过滤 仅当偏移变化超过阈值(默认 2.0pt)时才触发,减少抖动噪声。
- 后台 ↔ 主线程调度 在后台队列中采样,主线程上发布,确保 UI 更新安全且不卡顿。
- 可定制
自定义采样间隔(
sampleInterval
)和最小差值阈值(minDifference
)。 - 多轴 & 滚动条 支持监测水平、垂直或双轴滚动,并可选择性显示或隐藏滚动条。
-
在 Xcode 中选择 File → Swift Packages → Add Package Dependency...
-
输入仓库地址,例如:
https://github.com/asyncawaited/ScrollOffsetReader.git
-
指定版本区间(例如
from: "1.0.0"
)。
import SwiftUI
import ScrollOffsetReader
struct ContentView: View {
@State private var offset: CGSize = .zero
var body: some View {
VStack {
Text("偏移: x=\(offset.width, specifier: "%.1f"), y=\(offset.height, specifier: "%.1f")")
.padding()
ScrollOffsetReader(
scrollOffset: $offset,
axes: .vertical,
showsIndicators: false,
sampleInterval: 0.2, // 默认 0.25
minDifference: 1.0 // 默认 2.0
) {
LazyVStack {
ForEach(0..<100) { i in
Text("Item \(i)")
.frame(height: 50)
}
}
}
}
}
}
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
scrollOffset |
Binding<CGSize> |
— | 绑定外部状态,用于接收采样后的滚动偏移 |
axes |
Axis.Set |
.vertical |
要监测的滚动方向:.horizontal 、.vertical 或两者 |
showsIndicators |
Bool |
true |
是否显示滚动条 |
sampleInterval |
TimeInterval |
0.25 |
采样间隔(秒) |
minDifference |
CGFloat |
2.0 |
触发更新所需的最小偏移变化(pt) |
-
OffsetSampler
- 使用
CurrentValueSubject<CGSize, Never>
接收原始偏移; - 自定义
sample(with:)
将偏移与定时器结合,仅在定时器触发时取最新值; - 去重并在主线程通过
@Published var sampledOffset
发布。
- 使用
-
ScrollOffsetModifier
- 将
OffsetSampler
以@StateObject
持有; - 通过
PreferenceKey
捕获每帧偏移并调用update(offset:)
; - 监听
sampler.$sampledOffset
,将采样结果写回绑定的scrollOffset
。
- 将
-
ScrollOffsetReader
- 封装
ScrollView
+ZStack
+ScrollOffsetProxy
+ScrollOffsetModifier
,一行代码即可使用。
- 封装
- Swift: 5.9+
- iOS: 13.0+
- macOS: 11.0+
- Xcode: 15.0+
MIT © Royal