fermoya / SwiftUIPager

Native Pager in SwiftUI
MIT License
1.27k stars 168 forks source link

[BUG] Drag gesture on a view that contains a Pager #248

Closed Lawris closed 2 years ago

Lawris commented 2 years ago

Hello, first of all thanks for this fantastic package that I've been playing around with for the past few days! I went through all swipe-related issues without success, so I am openning an issue.

Describe the bug I have a "draggable" bottom sheet wich includes an horizontal pager inside. In order to be draggable (from top to bottom only), the bottom sheet uses .gesture(). It looks like the Pager and the gesture on the bottom sheet are colliding.

I can drag down the bottom sheet by swiping somewhere outside the pager, but when I try to drag down the bottom sheet by swipping on the Pager area, it does not work.

Here is a video to illustrate the current behavior:

https://user-images.githubusercontent.com/15719068/150989607-62f16cd0-c8fb-4d7a-8220-1dc69f9a57da.mov

Here is the code to replicate what's on the video:

struct ContentView: View {

    @StateObject var page: Page = .first()
    var items = Array(0..<5)
    var colors = [Color.red, Color.blue, Color.yellow, Color.green, Color.orange]

    var body: some View {
        SampleSheet(isOpen: .constant(true), maxHeight: 600) {

            VStack {
                Pager(page: page,
                      data: items,
                      id: \.self,
                      content: { index in

                    Rectangle()
                        .foregroundColor(colors[index])
                        .frame(width: 200, height: 300)
                        .cornerRadius(20)
                })
                    .swipeInteractionArea(.page)
                    .singlePagination(ratio: 1, sensitivity: .high)
                    .itemSpacing(-150)
            }
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct SampleSheet<Content: View>: View {

    @Binding var isOpen: Bool

    let maxHeight: CGFloat
    let minHeight: CGFloat
    let content: Content

    @GestureState private var translation: CGFloat = 0

    private var offset: CGFloat {
        isOpen ? 0 : maxHeight - minHeight
    }

    init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
        self.minHeight = maxHeight * 0.3
        self.maxHeight = maxHeight
        self.content = content()
        self._isOpen = isOpen
    }

    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 0) {
                self.content
            }
            .frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
            .background(Color.black)
            .frame(height: geometry.size.height, alignment: .bottom)
            .offset(y: max(self.offset + self.translation, 0))
            .animation(.interactiveSpring())
            .gesture(
                DragGesture().updating(self.$translation) { value, state, _ in
                    state = value.translation.height
                }.onEnded { value in
                    let snapDistance = self.maxHeight * 0.25
                    guard abs(value.translation.height) > snapDistance else {
                        return
                    }
                    self.isOpen = value.translation.height < 0
                }
            )

        }
    }
}

I tried replacing .gesture() with .highPriorityGesture(). It does work since it overrides the children gestures, but the Pager is not responsive anymore.

I also tried playing with .swipeInteractionArea(), .delaysTouches() and .pagingPriority() without success.

Maybe it is worth mentionning that using a HStack embedded in a ScrollView does not obstruct the vertical swipe of the upper view.

So my question is: is it possible to tweak the Pager in order for it to not prevent the vertical swipe of the upper view when swipping from the Pager area?

Any help is greatly appreciated.

fermoya commented 2 years ago

Have you tried using pagingPriority @Lawris ?

fermoya commented 2 years ago

This can be fixed if you use simulateneousGesture in your SampleSheet. Check it out:

https://user-images.githubusercontent.com/11335612/160168688-514611cd-1673-4ec4-b087-52882565168f.mov

Lawris commented 2 years ago

Just tested it now, it works flawlessly, thanks a lot for taking the time! Cheers.