16
votes

I have a UIScrollView with images and when the user scrolls to the end of the scrollview i want to update the content. This is my code to detect when the bottom of the scrollview is reached:

The code below is implemented in scrollViewDidScroll: delegate

CGFloat scrollViewHeight = imagesScrollView.bounds.size.height;
CGFloat scrollContentSizeHeight = imagesScrollView.contentSize.height;
CGFloat bottomInset = imagesScrollView.contentInset.bottom;
CGFloat scrollViewBottomOffset = scrollContentSizeHeight + bottomInset - scrollViewHeight;

if(imagesScrollView.contentOffset.y > scrollViewBottomOffset){


    [imagesView addSubview:imagesBottomLoadingView];

    [self downloadImages];

}

My problem is that when the user scrolls to bottom, my function is called several times, but i want to call it only once. I tried with imagesScrollView.contentOffset.y == scrollViewBottomOffset but it doesn't work and the function is not called

8
Add a condition flag to filter out extra call, for example, like if (imagesView has child imagesBottomLoadingView)xfx
ok, it was so easy but i didn't think this. Thank you!lubilis

8 Answers

2
votes

Have you thought of adding a boolean. update it when the method is called for the first time and maybe when user scrolls back up.

32
votes

If you want to detect them in swift:

override func scrollViewDidScroll(scrollView: UIScrollView) {

if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
    //reach bottom
}

if (scrollView.contentOffset.y < 0){
    //reach top
}

if (scrollView.contentOffset.y >= 0 && scrollView.contentOffset.y < (scrollView.contentSize.height - scrollView.frame.size.height)){
    //not top and not bottom
}}
6
votes

Carlos answer is better.

For Swift 4.x you must change method name:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
            //bottom reached
        }
    }
5
votes
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{
    float bottomEdge = scrollView.contentOffset.y + scrollView.frame.size.height;
    if (bottomEdge >= scrollView.contentSize.height) 
    {
        // we are at the end
    }
}
2
votes

Sometimes you will have to use +1 in the condition because the contentSize.height gives you a few decimals over, so if you use this, you avoid it...

override func scrollViewDidScroll(scrollView: UIScrollView) {

   if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
       //bottom reached
   }
}
0
votes

implement scrollViewDidScroll: and check contentOffset in that for reaching the end

0
votes

For Swift 4.5:

func scrollViewDidScroll(_ scrollView: UIScrollView) 
{    
   let scrollViewHeight = scrollView.frame.size.height
    let scrollContentSizeHeight = scrollView.contentSize.height
     let scrollOffset = scrollView.contentOffset.y

    if (scrollOffset == 0)
    {
        // then we are at the top
        print("then we are at the top")
    }
    else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
    {
        print("then we are at the end")
        // then we are at the end
    }      
}
0
votes

I used a mixed approach for this. Let me explain:

While your calculus is correct, the delegate your listening to is an overkill since scrollViewDidScroll is called many times which can lead to performance issues. You should (as I did) use scrollViewDidEndDragging and scrollViewDidEndDecelerating which are called only once at the end of each of their respective events.

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    // Scrolling acceleration didn't continue after the finger was lifted
    if !decelerate {
        executeActionAtTheEnd(of: scrollView)
    }
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    executeActionAtTheEnd(of: scrollView)
}

private func executeActionAtTheEnd(of scrollView: UIScrollView) {
    if scrollView.contentOffset.y + 1 >= (scrollView.contentSize.height - scrollView.frame.size.height) {
        // Do here whatever you want when end of scrolling is reached
    }
}