Introspect prevents ScrollView `.scrollPosition` to work properly
jorgej-ramos opened this issue ยท 2 comments
Description
It seems that the implementation of .introspect
is somehow blocking the correct behavior of .scrollPosition
in a ScrollView.
We can see it using the following example:
import SwiftUI
import SwiftUIIntrospect
struct SwiftUIView: View {
@State private var position: Int?
@StateObject private var scrollViewDelegate = ScrollViewDelegate()
var body: some View {
VStack {
ScrollView(.horizontal) {
LazyHStack {
ForEach(0..<100) { index in
Rectangle()
.fill(Color.green.gradient)
.frame(width: 60, height: 40)
.id(index)
}
}
.scrollTargetLayout()
}
.scrollPosition(id: $position)
.introspect(.scrollView, on: .iOS(.v17)) { scroll in
scroll.delegate = scrollViewDelegate
}
Text("Position: \(position ?? 0) | Scrolling: \(scrollViewDelegate.isScrolling ? "Yes" : "No")")
}
}
}
#Preview {
SwiftUIView()
}
@Observable
fileprivate class ScrollViewDelegate: NSObject, ObservableObject, UIScrollViewDelegate {
var isScrolling = false
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
isScrolling = true
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
isScrolling = decelerate ? true : false
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
isScrolling = false
}
}
If we implement .introspect
, we will stop receiving updates on position
.
If we don't implement it, we won't be able to use UIScrollViewDelegate
but instead .scrollPosition
will work again.
I have tried placing the statement .scrollPosition(id: $position)
before and/or after the implementation of .instrospect
, but the result is the same in all cases.
Also, this implementation has been tested on LazyHStack
before and after scrollTargetLayout()
with no success.
.introspect(.scrollView, on: .iOS(.v17), scope: .ancestor) {
$0.delegate = scrollViewDelegate
}
Checklist
- I have read the README before submitting this report.
- This issue hasn't been addressed in an existing GitHub issue or discussion.
Expected behavior
The expected behavior is that .scrollPosition
and .introspect
can coexist, in such a way that updates are received from both, and both the current position and whether the ScrollView is scrolling or not are displayed.
Actual behavior
Currently, it appears that .introspect
is overriding the ability to receive updates from .scrollPosition
Steps to reproduce
- Copy and paste the code in description on a new project.
- Comment and uncomment the
.introspect
modifier to see the differences.
Version information
1.1.0
Destination operating system
iOS 17
Xcode version information
Version 15.0.1 (15A507)
Swift Compiler version information
swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: x86_64-apple-macosx14.0
You can't just override the entire delegate. See #363 (comment) for details.
First of all, thank you for taking the time to respond.
Indeed, taking into account the UIScrollViewDelegate
that SwiftUI manages behind the scenes is mandatory so as not to break the cycles of SwiftUI's internal structure.
I had been mulling over the problem for so long that I hadn't thought about this. And it makes perfect sense. Is working.
Kudos to you for the help!