fermoya / SwiftUIPager

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

[FEAT] Add trackpad scroll support #286

Open almagest21 opened 2 years ago

almagest21 commented 2 years ago

Is your feature request related to a problem? Please describe. When using Trackpad, it requires drag and drop operation to change page.

Describe the solution you'd like For Desktop or iPad with Trackpad use-case, two finger scroll is more easier way than drag and drop page. Please consider to support to scroll pages.

Describe alternatives you've considered N/A

fermoya commented 2 years ago

Hi @almagest21 , thanks for your feedback. I believe it's not possible to do this natively in SwiftUI? I can't find any documentation on it, the only accepted answer is "wrapping an NSView into SwiftUI and use scrollWheel". However this defeats the purpose of the library 😕

iankoex commented 1 year ago

I have been able to find a workaround for this (sort off) I made changes to PagerContent. I have a variable @State private var mouseIsOnCards = false on stack I have

let stack = HStack(spacing: interactiveItemSpacing) {
    ForEach(dataDisplayed, id: id) { item in
        Group {
            if self.isInifinitePager && self.isEdgePage(item) {
                EmptyView()
            } else {
                self.content(item.element)
                    .frame(size: self.pageSize)
                    .scaleEffect(self.scale(for: item))
                    .rotation3DEffect((self.isHorizontal ? .zero : Angle(degrees: -90)) - self.scrollDirectionAngle,
                                      axis: (0, 0, 1))
                    .rotation3DEffect(self.angle(for: item),
                                      axis:  self.axis)
                    .opacity(opacity(for: item))
            }
        }
    }
    .offset(x: self.xOffset, y : self.yOffset)
}
.frame(size: size)
.onAppear(perform: monitorScrollWheelEvents)
.onHover { bool in
    withAnimation(.spring()) {
        mouseIsOnCards = bool
    }
}

and finally

private func monitorScrollWheelEvents() {
    #if os(macOS)
    NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { event in
        if mouseIsOnCards {
            if event.phase == .changed {
                let animation = draggingAnimation.animation
                withAnimation(animation) {
                    let x = (event.scrollingDeltaY)
                    self.pagerModel.draggingOffset =  self.draggingOffset + x
                    self.onDraggingChanged?(Double(-self.draggingOffset / self.pageDistance))
                    self.pagerModel.objectWillChange.send()
                }
            }
            if event.phase == .ended {
                onDragGestureEnded()
            }
        }
        return event
    }
    #endif
}

This is preliminary and I have not extensively tested it. That's why I can't PR. You will notice that many precautions implemented in func onDragChanged(with value: DragGesture.Value) are not present. This works for vertical Pager. If you want for horizontal you can use let x = (event.scrollingDeltaX)

milanvarady commented 1 year ago

This is the only feature holding me back from using this awesome library. It would be really nice if you somehow managed to add this.