100

In my scroll view, I want to get the current page that's being displayed (maybe page isn't the correct term). I can't find any variable that holds this. But I think it must be held somewhere, since the indicator is able to show which sub-view of the scroll view is currently being displayed.

Is this hidden from us completely or is there a way for me to access it?

TheNeil
  • 3,321
  • 2
  • 27
  • 52

14 Answers14

268

There is no UIScrollView property for the current page. You can calculate it with:

int page = scrollView.contentOffset.x / scrollView.frame.size.width;

If you want to round up or down to the nearest page, use:

CGFloat width = scrollView.frame.size.width;
NSInteger page = (scrollView.contentOffset.x + (0.5f * width)) / width;
jhabbott
  • 18,461
  • 9
  • 58
  • 95
AlexVogel
  • 10,601
  • 10
  • 61
  • 71
33

I pretty recommend you to use this code

int indexOfPage = scrollView.contentOffset.x / scrollView.frame.size.width;

but if you use this code your view doesn't need to be exactly on the page that indexOfPage gives you. It because I also recommend you to use this code only in this method

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{}

which is called when your scrollView finishes scrolling and to have number of your page really sharp

I recommend you to set your scrollView to paged enabled with this code

[scrollView setPagingEnabled:YES];

So finally it should look like that way

-(void) methodWhereYouSetYourScrollView
{
   //set scrollView
   [scrollView setPagingEnabled:YES];
   scrollView.delegate = self;
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
   int indexOfPage = scrollView.contentOffset.x / scrollView.frame.size.width;
   //your stuff with index
}
Jiří Zahálka
  • 8,070
  • 2
  • 21
  • 17
  • not working on iOS7 device (compiled with XCODE 6.2) contentOffset.x equals 639 instead of 640 (don't ask me why...) when scrollViewDidEndDecelerating. user363349 answer is correct : return floor((self.contentOffset.x - pageWidth / 2) / pageWidth) + 1; – Vassily Apr 13 '15 at 14:40
  • This does not work always. Sometimes I'm getting the page as 0 which is wrong in my case. – Sneha Nov 09 '17 at 12:44
31

In swift I would do it in extension:

extension UIScrollView {
    var currentPage:Int{
        return Int((self.contentOffset.x+(0.5*self.frame.size.width))/self.frame.width)+1
    }
}

Then just call:

scrollView.currentPage
Andrius Steponavičius
  • 8,074
  • 3
  • 22
  • 25
12

As above but as a category


@interface UIScrollView (CurrentPage)
-(int) currentPage;
@end
@implementation UIScrollView (CurrentPage)
-(int) currentPage{
    CGFloat pageWidth = self.frame.size.width;
    return floor((self.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
}
@end
user363349
  • 1,228
  • 14
  • 16
  • 7
    That's overdone, just use return round(self.contentOffset.x / self.frame.size.width); Much simpler and easier to understand.. – Leonard Pauli Apr 30 '13 at 14:06
  • -1 @LeonardPauli. The above implementation returns the page that is more than 50% visible. The naive implementation only changes page value when the scrollview's subview occupies the entire scrollview bounds. – user Mar 20 '14 at 03:54
  • 1
    @user Firstly, floor() rounds always down, and round() rounds up if decimal part is >= 5, else down. By that being said, floor(a)=round(a-0.5). Let's do some simple algebra. floor((a-b/2)/b)+1 = floor(a/b-0.5)+1 = round(a/b-0.5-0.5)+1 = round(a/b-1)+1 = round(a/b). Look! It seems like floor((a-b/2)/b)+1 = round(a/b)! Mathemagical. – Leonard Pauli Mar 20 '14 at 23:11
  • But yeah, if I for some reason would have used floor instead, then you would have been perfectly right! ;) – Leonard Pauli Mar 20 '14 at 23:13
10

Another way:

extension MyViewController: UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let width = scrollView.frame.width
        let page = Int(round(scrollView.contentOffset.x/width))
        print("CurrentPage:\(page)")
    }
}
Hemang
  • 26,840
  • 19
  • 119
  • 186
7

There's some good answers here already. However, for scenarios where content doesn't fit exactly into a page - and if like me you want to use the result for a UIPageControl, then it's essential to use the ceil function.

Let's take an example where I have "four and a bit" pages of content.

I'll base this on my current real life example. The content size width is 3140 points. Based upon my collection view frame size, page width is 728.

So number of pages equals:

 3140 / 728 = 4.313 

My numberOfPages is going to be five. Four of which will show in entirety, and the last of which - page five - will show that remaining 0.313 of content.

Now, numberOfPages being five means that the page indices will be 0, 1, 2, 3, and 4.

When I swipe rightwards, paging towards the final offset, the scrollViewDidEndDecelerating method gives a final X offset of 2412.

Applying the rounding calculation:

2412 / 728 = 3.313 then rounded = 3

That's incorrect. Page user is viewing by offset should be:

Offset / Page User Is Viewing
0        0
728      1
1456     2
2184     3
2412     4

The correct calculation using ceil:

private func pageForOffset(offset:CGFloat, pageWidth width:CGFloat) -> Int
{
    let page = Int(ceil(offset / width))
    NSLog("\(__FUNCTION__) offset \(offset) width \(width) page \(page) ")
    return page
}
Max MacLeod
  • 26,115
  • 13
  • 104
  • 132
6

Just divide the current offset by the page size:

CGFloat pageNum = (int)(scrollView.contentOffset.x / scrollView.frame.size.width);
aoakenfo
  • 914
  • 1
  • 7
  • 9
4

The solution of Aoakenfo is amazing, this is another solution combining your code with the function scrollViewDidScroll and PageController

- (void)scrollViewDidScroll:(UIScrollView *)sender {
    if (sender == self.scroll) {
        int pageNum = (int)(self.scrPage.contentOffset.x / self.scrPage.frame.size.width);
        self.pagecontroller.currentPage =pageNum;
    }
}
jose920405
  • 7,982
  • 6
  • 45
  • 71
pabloverd
  • 614
  • 8
  • 8
  • 1
    This is greedy, not lazy... It also isn't MVC. A much simpler way is to invalidate the currentPage on scroll, and set it only when it's requested... all from a subclass or category on UIScrollView –  Jun 05 '13 at 23:10
4

For swift I would just use this

    let width = scrollView.frame.width
    let page = round(scrollView.contentOffset.x/width)

Pretty straightforward, it basically takes the scrollview x position, divides it by the scrollview width, then rounds it.

neptunes
  • 72
  • 5
3

In xCode 7.x swift 2.x you can do :

//MARK: - ScrollView Extensions
// Get the current page number
extension UIScrollView {
    var currentPage: Int {
        return Int(round(self.contentOffset.x / self.bounds.size.width))
    }

// If you have reversed offset (start from contentSize.width to 0)
    var reverseCurrentPage: Int {
        return Int(round((contentSize.width - self.contentOffset.x) / self.bounds.size.width))-1
    }
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
2

I extracted this out of a couple of our apps...

- (NSInteger)currentPage
{
    if (0 == self.frame.size.width) {
        NSLog(@"frame width == 0!");
        return 0;
    }
    if (! self.currentPageValid) {
        _currentPage = round(self.contentOffset.x / self.frame.size.width);
        self.currentPageValid = YES;
    }
    return _currentPage;
}

Full source here

1

Are you using a UIPageControl? If so, this has a currentPage property. If not, I think you'll need to calculate the page index from the scrollView offset.

Rengers
  • 14,911
  • 1
  • 36
  • 54
1

Swift 5.1+ UIScrollView extension (Omitting the return keyword)

extension UIScrollView {

    var currentPage: Int {
        Int(round(self.contentOffset.x / self.frame.width))
    }
}
Diego Carrera
  • 2,245
  • 1
  • 13
  • 16
0

If Any one Looking for way of doing in C# for Xamarin

    public int CurrentIndex
    {
        get => (int)Math.Round(this.scrollView.ContentOffset.X / this.scrollView.Frame.Width);
        set => this.scrollView.ScrollRectToVisible(new CGRect(new CGPoint(this.scrollView.Frame.Size.Width * value, 0), this.scrollView.Frame.Size), true);
    }

This getter and Setter should provide you current page as well let you scroll to the specified page

soan saini
  • 230
  • 3
  • 9