Created
February 5, 2023 20:31
-
-
Save LucasAbijmil/3cf147c750b806f4a56eea630d648b2b to your computer and use it in GitHub Desktop.
Detect if the user scrolls in SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ScrollViewDelegate<Content: View>: View { | |
@State private var verticalScrollTimer: Timer? | |
@State private var horizontalScrollTimer: Timer? | |
@ViewBuilder private let content: Content | |
private let axes: Axis.Set | |
private let showsIndicators: Bool | |
private let onVerticalScroll: ((_ isScrolling: Bool) -> Void)? | |
private let onHorizontalScroll: ((_ isScrolling: Bool) -> Void)? | |
private let coordinateSpace = "scrollView" | |
private let scrollDetectorInterval = 0.25 | |
init(_ axes: Axis.Set = .vertical, | |
showsIndicators: Bool = true, | |
@ViewBuilder content: () -> Content, | |
onVerticalScroll: ((_ isScrolling: Bool) -> Void)? = nil, | |
onHorizontalScroll: ((_ isScrolling: Bool) -> Void)? = nil) { | |
self.axes = axes | |
self.showsIndicators = showsIndicators | |
self.content = content() | |
self.onVerticalScroll = axes.contains(.vertical) ? onVerticalScroll : nil | |
self.onHorizontalScroll = axes.contains(.horizontal) ? onHorizontalScroll : nil | |
} | |
var body: some View { | |
ScrollView(axes, showsIndicators: showsIndicators) { | |
content | |
.background { | |
GeometryReader { proxy in | |
Color.clear.preference(key: YOffsetKey.self, value: -proxy.frame(in: .named(coordinateSpace)).minY) | |
Color.clear.preference(key: XOffsetKey.self, value: -proxy.frame(in: .named(coordinateSpace)).minX) | |
} | |
} | |
} | |
.coordinateSpace(name: coordinateSpace) | |
.onPreferenceChange(YOffsetKey.self) { value in | |
guard axes.contains(.vertical) else { return } | |
verticalScrollTimer?.invalidate() | |
verticalScrollTimer = Timer.scheduledTimer(withTimeInterval: scrollDetectorInterval, repeats: false) { _ in | |
onVerticalScroll?(false) | |
} | |
onVerticalScroll?(value > 0) | |
} | |
.onPreferenceChange(XOffsetKey.self) { value in | |
guard axes.contains(.horizontal) else { return } | |
horizontalScrollTimer?.invalidate() | |
horizontalScrollTimer = Timer.scheduledTimer(withTimeInterval: scrollDetectorInterval, repeats: false) { _ in | |
onHorizontalScroll?(false) | |
} | |
onHorizontalScroll?(value > 0) | |
} | |
} | |
} | |
private struct YOffsetKey: PreferenceKey { | |
static var defaultValue: CGFloat = .zero | |
static func reduce(value: inout Value, nextValue: () -> Value) { | |
value += nextValue() | |
} | |
} | |
private struct XOffsetKey: PreferenceKey { | |
static var defaultValue: CGFloat = .zero | |
static func reduce(value: inout Value, nextValue: () -> Value) { | |
value += nextValue() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment