@@ -14,11 +14,11 @@ public protocol MSCollectionViewPagingDataSource: AnyObject {
1414 /// Will be called whenever the pager needs the offset of a specific index
1515 func collectionViewPaging( _ collectionViewPaging: MSCollectionViewPaging , offsetForItemAtIndex index: Int ) -> CGFloat
1616
17- /// Will be called whenever the pager needs an index at a specific offset
17+ /// Will be called whenever the pager needs an index at a specific offset.
1818 func collectionViewPaging( _ collectionViewPaging: MSCollectionViewPaging , indexForItemAtOffset offset: CGFloat ) -> Int
1919
2020 /// The minimum velocity required to jump to the adjacent item
21- func collectionViewPagingScrollThreshold ( _ collectionViewPaging: MSCollectionViewPaging ) -> CGFloat
21+ func collectionViewPagingVelocityThreshold ( _ collectionViewPaging: MSCollectionViewPaging ) -> CGFloat
2222
2323 /// The minimum number of items to scroll
2424 func collectionViewPagingMinimumItemsToScroll( _ collectionViewPaging: MSCollectionViewPaging ) -> Int ?
@@ -30,29 +30,14 @@ public protocol MSCollectionViewPagingDataSource: AnyObject {
3030 func collectionViewNumberOfItems( _ collectionViewPaging: MSCollectionViewPaging ) -> Int
3131}
3232
33- // Default arguments
34- extension MSCollectionViewPagingDataSource {
35- public func collectionViewPagingScrollThreshold( _ collectionViewPaging: MSCollectionViewPaging ) -> CGFloat {
36- return 0.2
37- }
38-
39- public func collectionViewPagingMinimumItemsToScroll( _ collectionViewPaging: MSCollectionViewPaging ) -> Int ? {
40- return nil
41- }
42-
43- public func collectionViewPagingMaximumItemsToScroll( _ collectionViewPaging: MSCollectionViewPaging ) -> Int ? {
44- return nil
45- }
46- }
47-
4833public class MSCollectionViewPaging : NSObject {
4934
5035 weak var dataSource : MSCollectionViewPagingDataSource ?
5136
5237 var currentContentOffset : CGFloat = 0
5338
54- var scrollThreshold : CGFloat {
55- return dataSource? . collectionViewPagingScrollThreshold ( self ) ?? 0
39+ var velocityThreshold : CGFloat {
40+ return dataSource? . collectionViewPagingVelocityThreshold ( self ) ?? 0
5641 }
5742
5843 var numberOfItems : Int {
@@ -65,60 +50,63 @@ public class MSCollectionViewPaging: NSObject {
6550
6651 func getNewTargetOffset( startingOffset: CGFloat , velocity: CGFloat , targetOffset: CGFloat ) -> CGFloat {
6752
68- // Get the current index and target index based on the offset
69- let currentIndex = dataSource ? . collectionViewPaging ( self , indexForItemAtOffset : startingOffset ) ?? 0
70- let targetIndex = dataSource ? . collectionViewPaging ( self , indexForItemAtOffset : targetOffset ) ?? 0
53+ // Check the velocity, if it's greater than the threshold, move at least 1 cell in the direction of the velocity
54+ switch abs ( velocity ) {
55+ case let v where v > velocityThreshold :
7156
72- let imAtFistItemAndScrollingBack = currentIndex == 0 && velocity < 0
73- let imAtLastItemAndScrollingForward = currentIndex == numberOfItems && velocity > 0
57+ // Get the current index and target index based on the offset
58+ let currentIndex = dataSource? . collectionViewPaging ( self , indexForItemAtOffset: startingOffset) ?? 0
59+ let targetIndex = dataSource? . collectionViewPaging ( self , indexForItemAtOffset: targetOffset) ?? 0
7460
75- guard !imAtFistItemAndScrollingBack && !imAtLastItemAndScrollingForward else { return startingOffset }
61+ // Making sure not to scroll to non-existing indices
62+ let imAtFistItemAndScrollingBack = currentIndex == 0 && velocity < 0
63+ let imAtLastItemAndScrollingForward = currentIndex == numberOfItems && velocity > 0
7664
77- let delta = targetIndex - currentIndex
65+ guard !imAtFistItemAndScrollingBack && !imAtLastItemAndScrollingForward else { return startingOffset }
7866
79- var offset : Int
80- switch ( currentIndex, targetIndex, abs ( velocity) ) {
81- // If there was no change in indices but the velocity is higher than the threshold, move to adjacent cell
82- case let ( x, y, v) where x == y && v > scrollThreshold:
83- offset = 1
67+ // Making sure we move at least 1 cell
68+ var offset = max ( targetIndex - currentIndex, 1 )
8469
85- // Otherwise, get the differece between the target and the current indices
86- default :
87- offset = abs ( delta )
88- }
70+ // If we've set a minimum number of items to scroll, enforce it
71+ if let minimumItemsToScroll = dataSource ? . collectionViewPagingMinimumItemsToScroll ( self ) , offset != 0 {
72+ offset = max ( offset , minimumItemsToScroll )
73+ }
8974
90- /// If we've set a minimum number of items to scroll, enforce it
91- if let minimumItemsToScroll = dataSource? . collectionViewPagingMinimumItemsToScroll ( self ) , offset != 0 {
92- offset = max ( offset, minimumItemsToScroll )
93- }
75+ // If we've set a maximum number of items to scroll, enforce it
76+ if let maximumItemsToScroll = dataSource? . collectionViewPagingMaximumItemsToScroll ( self ) {
77+ offset = min ( offset, maximumItemsToScroll )
78+ }
9479
95- /// If we've set a maximum number of items to scroll, enforce it
96- if let maximumItemsToScroll = dataSource? . collectionViewPagingMaximumItemsToScroll ( self ) {
97- offset = min ( offset, maximumItemsToScroll)
98- }
80+ // The final index is the current index ofsetted by the value and in the velocity direction
81+ var finalIndex = currentIndex + ( offset * Sign( value: velocity) . multiplier)
9982
100- // The final index is the current index ofsetted by the value and in the velocity direction
101- var finalIndex = currentIndex + ( offset * Sign( value: delta) . multiplier)
83+ let indexExists = finalIndex < numberOfItems
84+ // Move to index only if it exists. This will solve issues when there are multiple items in the same page
85+ if !indexExists {
86+ finalIndex = currentIndex
87+ }
88+ return dataSource? . collectionViewPaging ( self , offsetForItemAtIndex: finalIndex) ?? 0
10289
103- let indexExists = finalIndex < numberOfItems
104- // Move to index only if it exists. This will solve issues when there are multiple items in the same page
105- if !indexExists {
106- finalIndex = currentIndex
107- }
90+ default :
10891
109- return dataSource? . collectionViewPaging ( self , offsetForItemAtIndex: finalIndex) ?? 0
92+ let finalIndex = dataSource? . collectionViewPaging ( self , indexForItemAtOffset: targetOffset) ?? 0
93+ return dataSource? . collectionViewPaging ( self , offsetForItemAtIndex: finalIndex) ?? 0
94+ }
11095 }
11196
11297 public func collectionViewWillEndDragging( scrollDirection: UICollectionView . ScrollDirection , withVelocity velocity: CGPoint , targetContentOffset: UnsafeMutablePointer < CGPoint > ) {
98+ var newOffset : CGFloat
11399 switch scrollDirection {
114100 case . horizontal:
115- targetContentOffset . pointee = CGPoint ( x : getNewTargetOffset ( startingOffset: currentContentOffset, velocity: velocity. x, targetOffset: targetContentOffset. pointee. x) , y : targetContentOffset . pointee . y )
116- currentContentOffset = targetContentOffset. pointee. x
101+ newOffset = getNewTargetOffset ( startingOffset: currentContentOffset, velocity: velocity. x, targetOffset: targetContentOffset. pointee. x)
102+ targetContentOffset . pointee = CGPoint ( x : newOffset , y : targetContentOffset. pointee. y )
117103 case . vertical:
118- targetContentOffset . pointee = CGPoint ( x : targetContentOffset . pointee . x , y : getNewTargetOffset ( startingOffset: currentContentOffset, velocity: velocity. y, targetOffset: targetContentOffset. pointee. y) )
119- currentContentOffset = targetContentOffset. pointee. y
104+ newOffset = getNewTargetOffset ( startingOffset: currentContentOffset, velocity: velocity. y, targetOffset: targetContentOffset. pointee. y)
105+ targetContentOffset . pointee = CGPoint ( x : targetContentOffset. pointee. x , y : newOffset )
120106 default :
121107 assertionFailure ( " Not Implemented " )
108+ newOffset = 0
122109 }
110+ currentContentOffset = newOffset
123111 }
124112}
0 commit comments