I'm trying to replicate a behavior from one of Apple's apps.

I figure this is a combination of scrolling and draging gestures.
By calculating the scroll offset, scroll direction and the position of the hidden view I managed to create a janky version of the behavior.
This is the code:
struct _TestView: View {
@State var scrollOffset: CGPoint = .zero
@State var contentOffset: CGPoint = CGPoint(x: .zero, y: -200)
@State var scrollDisabled = true
var drag: some Gesture {
DragGesture()
.onChanged { value in
if !scrollDisabled { return } // if scrolling is enabled then don't change content offset
var newLocation = startLocation ?? contentOffset
// sets dragging bounds (-200 to 0)
if contentOffset.y >= 0 && value.translation.height > 0 {
newLocation.y = 0;
} else if contentOffset.y <= -200 && value.translation.height < 0 {
newLocation.y = -200;
} else {
newLocation.y += value.translation.height
}
self.contentOffset = newLocation
}
.updating($startLocation) { ... }
}
// this is misc.
@GestureState private var fingerLocation: CGPoint? = nil
@GestureState private var startLocation: CGPoint? = nil
var fingerDrag: some Gesture { ... }
var body: some View {
OffsetObservingScrollView(offset: $scrollOffset) {
VStack {
Rectangle()
.fill(.blue)
.frame(height: 200)
Rectangle()
.fill(.red)
.frame(height: 1000)
.overlay { ... }
}
.offset(y: contentOffset.y)
.gesture(drag.simultaneously(with: fingerDrag))
}
.scrollDisabled(scrollDisabled)
.navigationTitle("Hello, world!")
.border(.black)
}
}
When scrolling is disabled, drag gestures work.

However when you fling scroll the drag is janky. Would love to know how to improve this :)
The next problem is how to enable scrolling when contentOffset == -200 or 0 I managed to get it to work somewhat by observing changes to scrollOffset, contentOffset and even the drag gesture values, but it's not reliable and barely works. :(
Appreciate all help!
If you just trying to replicate the behavior where you could hide a view behind like the Books app, it could be achieved by putting two LazyVStack inside a ScrollView where the first LazyVStack will have a pinned view for the behavior.
The code would be something like:
ScrollView(.vertical, showsIndicators: true) {
LazyVStack(pinnedViews: [.sectionHeaders]) {
Section {
Text("10 books 4 pdfs and more!")
} header: {
VStack {
Divider()
Text("Collections (Pinned)")
Divider()
}
}
}
LazyVStack() {
// Here goes the book grid
}
}
But I also noticed that in the Apple Books app, there is more detail to it, for example, the top view is hidden by default, and the navigation title won't collapse until the "hidden" view is fully invisible, plus the scroll snap behavior for the first section.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With