Skip to content

Instantly share code, notes, and snippets.

@shnhrrsn
Created February 10, 2023 02:39
Show Gist options
  • Save shnhrrsn/52b552980f55e023a8bc9c09194c8964 to your computer and use it in GitHub Desktop.
Save shnhrrsn/52b552980f55e023a8bc9c09194c8964 to your computer and use it in GitHub Desktop.
Simple SwiftUI onScroll Workaround
extension View {
func onScroll(_ isScrolling: Binding<Bool>) -> some View {
modifier(OnScrollModifier(isScrolling: isScrolling))
}
}
private struct OnScrollModifier: ViewModifier {
@State private var callbackID = 0
@Binding var isScrolling: Bool
func body(content: Content) -> some View {
content
.onAppear {
let callbackID = nextCallbackID()
listenForScrollStart(callbackID)
listenForScrollEnd(callbackID)
}
.onDisappear {
nextCallbackID()
}
}
private func listenForScrollStart(_ callbackID: Int? = nil) {
let callbackID = callbackID ?? nextCallbackID()
RunLoop.main.perform(inModes: [.tracking]) {
guard callbackID == self.callbackID else {
return
}
isScrolling = true
listenForScrollEnd()
}
}
private func listenForScrollEnd(_ callbackID: Int? = nil) {
let callbackID = callbackID ?? nextCallbackID()
RunLoop.main.perform { // Main runloop doesn't fire while user is scrolling
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
guard callbackID == self.callbackID else {
return
}
isScrolling = false
listenForScrollStart()
}
}
}
@discardableResult
private func nextCallbackID() -> Int {
callbackID += 1
return callbackID
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment