fermoya / SwiftUIPager

Native Pager in SwiftUI
MIT License
1.31k stars 172 forks source link

[BUG] Quick sliding to the top and bottom, do not adjust this method "onPageChanged" #303

Open JingChen1 opened 2 years ago

JingChen1 commented 2 years ago

Quick sliding, do not adjust this method "onPageChanged"

geraldvoit commented 1 year ago

I can confirm that there is an issue with the 'onPageChanged' handler.

Describe the bug

The 'onPageChanged' callback (PagerContent.swift:232) is not called in the event of gestures followed quickly by each other. Therefore, the app is not able to react on index change updates.

To Reproduce

Expected behavior

'onPageChanged' handler will be called on every page index change.

Environment:

OS: iOS 16 SwiftUIPager 2.5.0

Additional context

The SwiftUIPager detects both Drag Gestures and finishes both with 'onDragGestureEnded' (PagerContent.swift:373). The 'onAnimationCompleted' (PagerContent.swift:227) observer triggers only once, which is totally fine. But the second 'onDragGestureEnded' call sets self.pagerModel.pageIncrement = 0. Therefore, the following 'onAnimationCompleted' observation won't trigger the 'onPageChanged' callback.

PatrickGaissert commented 1 year ago

We really need this fix. For now we're pointing to the forked commit (thank you @geraldvoit !!!) but we would really like to have the fix in an official release of the library. Is there any chance this will happen?

ghost commented 8 months ago

Hello, @fermoya. Can you create a new release of the library with this fix, please🙏

alexshekhovtsov commented 1 month ago

Hello, guys, if somebody needs to fix this issue quickly, so just instead of using .onPageChanged on Pager

use this:

.onDraggingEnded {
    viewModel.onDraggingEnded()
}

and this workaround in your ViewModel:

private var pageWorkItem: DispatchWorkItem?
private var workItemPageIndex: Int = 0

func onDraggingEnded() {
    pageWorkItem?.cancel()

    pageWorkItem = DispatchWorkItem {
        self.handleOnDraggingEnded()
    }
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3,
                                  execute: pageWorkItem!)
}

private func handleOnDraggingEnded() {
    guard workItemPageIndex != currentPage.index else { return }
    updatePage()
    workItemPageIndex = currentPage.index
}