fermoya / SwiftUIPager

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

[REQUEST] multiplePagination should page based on scroll velocity #127

Closed joekndy closed 3 years ago

joekndy commented 4 years ago

Describe the bug Set page size instead of just the item size

To Reproduce create a a Pager with small items that aren't full width. swiping assumes the items are the size of the screen

Expected behavior Instead of just being able to set the item size, I should be able to set the page size, so swiping to the next item feels faster than if it were the entire page. It takes a lot of force on small items because the Pager always assumes that a page is close to the entire width of the device.

Video RPReplay_Final1600805259.MP4.zip

fermoya commented 4 years ago

Hi @joekndy , for small items like that I always recommend using swipeableArea(.allAvailable) and multiplePagination. I assume this isn't what you mean, is it?

Single pagination (default) assumes swiping from side to side will increment the page index by +- 1. With multiple pagination this isn't true anymore, the offset will just move the items and wherever it ends, that will be the index.

What is it exactly that you're doing at the very end of the video?

joekndy commented 4 years ago

@fermoya Thanks for the quick reply! I've set both of those and it works better, but feels somewhat unnatural. When I set the frame of the Pager to the size of the page, it feels great, but clips the other pages that are out of bounds. This works for now but would definitely be great to set the page size, or some way to change how scroll velocity effects how far it scrolls!

fermoya commented 4 years ago

@joekndy you can also use itemSpacing to separate the items between each other so that they don't clip. There's also a modifier called pagingAnimation to change the animation based on the total offset and velocity, if that helps too.

I'm having trouble understanding what you mean by "page size". So in the video you shared, Pager size there is the full screen, the item size would be the circle size. What's the "page size" there? Is it the necessary distance to swipe to next element?

joekndy commented 4 years ago

@fermoya Yeah it'd be the necessary distance to swipe to next element. It assumes it takes the full width of the screen to swipe to next page without multiplePagination set, but multiplePagination doesn't quite take into account the items size and how far it should scroll depending on velocity. Maybe pagingAnimation is what I could use to make it scroll faster? Just not sure how to configure it

joekndy commented 4 years ago

The instagram camera effects carousel is a great example! RPReplay_Final1600807750.MP4.zip

joekndy commented 4 years ago

@fermoya from playing with pagingAnimation, it seems that it just effects the animation once the paging has ended vs affecting how far the pages can scroll based on scroll velocity

fermoya commented 4 years ago

@joekndy apologies, it's been very busy with iOS 14 release. I have you this in mind, nonetheless, don't worry.

I see what you mean. What you want to change is the animation so that it runs faster. pagingAnimation lets you customize the animation based on dragging offset and speed. By default, multiplePagination uses steep(duration: 0.2). This gives you this Bezier Curve as a result. Then, Pager adapts the speed based on the number of pages scrolled. So if you've scrolled 4 pages or more, the speed is 0.25 which makes the animation be 0.2 / 0.25 = 0.8 seconds.

So what you can do to make it scroll faster is this:

`.pagingAnimation { _ in
    return .steep(0.1) // This way, the animation will be 0.4seconds tops
}

Or something you can also do is come up with your own bezier curve so that the the effect is the one you want. Does that make sense? If you can come up with some cool effect I can even add it to the library. For example, .steep is coded like this:

Animation.timingCurve(0.2, 1, 0.9, 1, duration: duration) // by default, duration is 0.2

If you change the 4 first parameters you can create your own. With this one I wanted to have a very quick scroll at first and ease out slowly at the end. I think if you use .steep(0.1) or .steep(0.15)or try different values you might achieve what you want.

Please, let me know your thoughts.