36

I tried finding this question for a while but could not find this problem's answer. My problem is that i have a UICollectionView and the Scroll Direction is Horizontal with Paging Enabled. My problem is that i want to keep the tack of the current page number on which the user is, so i created an int variable and now want to add or subtract it by 1 each time the user swipes right or left. I tried using scrollView's delegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

but when the user swipes right or left, it is called as many number of the times as the number of columns on a page in the UICollectionView plus it wont let me know that whether the user went to the next page or the previous one.

Ahsan Ebrahim Khatri
  • 1,807
  • 1
  • 20
  • 27
  • Possible duplicate of [Detecting UIScrollView page change](https://stackoverflow.com/questions/5272228/detecting-uiscrollview-page-change) – shim Aug 30 '18 at 16:11
  • Answered **FULLY** here: https://stackoverflow.com/a/59057080/294884 – Fattie Nov 26 '19 at 18:13

8 Answers8

45

Swift 3 Xcode 8.2

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let x = scrollView.contentOffset.x
        let w = scrollView.bounds.size.width
        let currentPage = Int(ceil(x/w))
        // Do whatever with currentPage.
}
shim
  • 9,289
  • 12
  • 69
  • 108
Ahmed Safadi
  • 4,402
  • 37
  • 33
44

Use :

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    CGFloat pageWidth = collectionView.frame.size.width;
    float currentPage = collectionView.contentOffset.x / pageWidth;

    if (0.0f != fmodf(currentPage, 1.0f))
    {
        pageControl.currentPage = currentPage + 1;
    }
    else
    {
        pageControl.currentPage = currentPage;
    }

    NSLog(@"Page Number : %ld", (long)pageControl.currentPage);
}

And if you are not using any pageControl, then ceil(currentPage) will be your current page number.

itsji10dra
  • 4,603
  • 3
  • 39
  • 59
  • this works perfectly as need, just couldnt understand the conditions so changed them to my own, but there is a problem, this method is called after the delegates of `UICollectionView` `- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath` but my page number variable changes in the delegate of `ScrollView` so its not working as needed, any solution to that ? – Ahsan Ebrahim Khatri Apr 08 '15 at 07:09
  • 1
    Thats a normal behaviour, this method will only fire up when your collectionview scrolling stops..!! – itsji10dra Apr 08 '15 at 07:12
  • yeah you are right.. i'll try to find some alternative for my situation but for the question, this is the perfect answer. Thanks for the help. – Ahsan Ebrahim Khatri Apr 08 '15 at 07:18
  • Doesn't work unfortunately. You have to cover scrollViewDidEndDragging as well. https://stackoverflow.com/a/59057080/294884 – Fattie Nov 26 '19 at 18:14
  • Unfortunately this doesn't work, at least not in iOS 15, for every case where the scroll view updates. For example, `scrollToItem` won't trigger this, even if animated is set to true. (The often recommended `scrollViewDidEndScrollingAnimation` doesn't work, either, because animations can be cancelled for other reasons). –  Sep 13 '22 at 02:55
12

Based on @Shankar BS 's answer I've implemented it like this in Swit. Keep in mind that CollectionViewDelegate conforms to ScrollViewDelegate:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let pageWidth = scrollView.frame.size.width
    let page = Int(floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
    print("page = \(page)")
}
Graham Perks
  • 23,007
  • 8
  • 61
  • 83
Andrej
  • 7,266
  • 4
  • 38
  • 57
8

You can get the current page like below, index will be from 0 to (total page - 1)

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
 {
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    NSLog(@"Current page -> %d",page);
}
shim
  • 9,289
  • 12
  • 69
  • 108
Shankar BS
  • 8,394
  • 6
  • 41
  • 53
  • Thanks a bunch.. :) is there any way that this method gets called before the `UICollectionView's` delegate `- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath` ? – Ahsan Ebrahim Khatri Apr 08 '15 at 07:18
8

SWIFT 5

For example, put this method in your ViewController with extensions UICollectionViewDelegate,UICollectionViewDataSource

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let pageWidth = scrollView.frame.size.width
        let page = Int(floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
        print("page = \(page)")
    }
Álvaro Agüero
  • 4,494
  • 1
  • 42
  • 39
4

Swift 2.2

 func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    let x = collectionView.contentOffset.x
    let w = collectionView.bounds.size.width
    let currentPage = Int(ceil(x/w))
    print("Current Page: \(currentPage)")
}
0

You can get the currentPage this way when the user end the dragging:

Swift 5.6 Tested and works!

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let itemWidth = collectionView.frame.width // Get the itemWidth. In my case every cell was the same width as the collectionView itself
    let contentOffset = targetContentOffset.pointee.x

    let targetItem = lround(Double(contentOffset/itemWidth))
    let targetIndex = targetItem % YOUR_ARRAY.count // numberOfItems which shows in the collection view
    print(targetIndex)
}
SwiftiSwift
  • 7,528
  • 9
  • 56
  • 96
-1

Xamarin.iOS:

You can override DecelerationEnded method of UICollectionViewDelegate or subscribe to DecelerationEnded event of UICollectionView (custom delegate will be used under the hood).

public override void DecelerationEnded(UIScrollView scrollView)
{
    var x = scrollView.ContentOffset.X;
    var w = scrollView.Bounds.Size.Width;
    var currentPage = Math.Ceiling(x / w);

    // use currentPage here
}
Yauheni Pakala
  • 868
  • 9
  • 16