ios – SwiftUI transmit drag focus to outer view (ScrollView inside dragable)

[ad_1]

What I need

I’ve a “ScrollView” in a customized sheet that I can drag down to shut. After all, the ScrollView is above the drag space, so after I drag over the ScrollView it scrolls down or up. I wish to disable the ScrollView when I’m on the high of the ScrollView and scroll up in order that the sheet is dragged and begins to shut. That is just like the behaviour of the Shazam sheet.

The Drawback

If the ScrollView is deactivated, the present drag motion just isn’t utilized to the sheet, however does nothing. Solely when dragging once more (on the now deactivated ScrollView) the sheet is concentrated on the dragging. So is there a approach to switch the main target of the drag motion from the ScrollView to the outer sheet view with out beginning a brand new one?

Showcase

I created a simplified model of the issue for higher understanding.

We’ve a container (inside) that’s draggable alongside the y-axis. On drag launch, we let it bounce again to offset 0.

@State var offsetY: CGFloat = .zero

    var physique: some View {
        Interior(outerOffset: $offsetY)
            .offset(y: offsetY)
            .gesture(DragGesture().onChanged { worth in
                offsetY = worth.translation.peak
            }.onEnded { _ in
                offsetY = 0
            }
            )
    }

Contained in the container now we have our personal ScrollView (ScrollViewOffset) which takes a callback with the present scroll offset. If the offset is optimistic (i.e. if we’re scrolling upwards, even when we’re on the high of the content material), we wish to disable the scroll and let the outer container drag alongside the y-axis as a substitute of the ScrollView. To activate the ScrollView we pay attention for the outerOffset (the drag worth) and activate the ScrollView when the offset is 0 once more (this occurs when releasing the drag as described earlier than).

struct Interior: View {
    @Binding var outerOffset: CGFloat
    @State var disableScroll = false

    var physique: some View {
        ScrollViewReader { proxy in
            ScrollViewOffset {
                ForEach(0 ... 10, id: .self) { e in
                    Textual content(String(e))
                        .id(e)
                        .padding()
                    Divider()
                }
            } onOffsetChange: { offset in
                if offset > 0 {
                    disableScroll = true
                }
            }
            .background(.purple)
            .padding()
            .body(width: nil, peak: 400)
            .onChange(of: outerOffset) { _ in
                if outerOffset == 0 {
                    proxy.scrollTo(0)
                    disableScroll = false
                }
            }
            .disabled(disableScroll)
        }
    }
}

Once we run this, it’s straightforward to see the issue I discussed earlier than. The ScrollView is getting disabled because it ought to, however the container just isn’t centered on the drag. Solely after beginning a brand new drag, the container is concentrated.

Demo showcase

My precise case

Final however not least the shazam equal (left) and my undertaking (proper).

Shazam Demo
Prop Demo

[ad_2]

Leave a Reply