Created
February 10, 2023 02:39
-
-
Save shnhrrsn/52b552980f55e023a8bc9c09194c8964 to your computer and use it in GitHub Desktop.
Simple SwiftUI onScroll Workaround
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
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