How to detect when a UIScrollView has finished scrolling

UIScrollViewDelegate has got two delegate methods scrollViewDidScroll: and scrollViewDidEndScrollingAnimation: but neither of these tell you when scrolling has completed. scrollViewDidScroll only notifies you that the scroll view did scroll not that it has finished scrolling.

The other method scrollViewDidEndScrollingAnimation only seems to fire if you programmatically move the scroll view not if the user scrolls.

Does anyone know of scheme to detect when a scroll view has completed scrolling?


The 320 implementations are so much better - here is a patch to get consistent start/ends of the scroll.

-(void)scrollViewDidScroll:(UIScrollView *)sender 
{   
[NSObject cancelPreviousPerformRequestsWithTarget:self];
    //ensure that the end of scroll is fired.
    [self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:sender afterDelay:0.3]; 

...
}

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
...
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

- (void)stoppedScrolling {
    // ...
}

This has been described in some of the other answers, but here's (in code) how to combine scrollViewDidEndDecelerating and scrollViewDidEndDragging:willDecelerate to perform some operation when scrolling has finished:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
                  willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

- (void)stoppedScrolling
{
    // done, do whatever
}

For all scrolls related to dragging interactions, this will be sufficient:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    _isScrolling = NO;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        _isScrolling = NO;
    }
}

Now, if your scroll is due to a programmatic setContentOffset/scrollRectVisible (with animated = YES or you obviously know when scroll is ended):

 - (void)scrollViewDidEndScrollingAnimation {
     _isScrolling = NO;
}

If your scroll is due to something else (like keyboard opening or keyboard closing), it seems like you'll have to detect the event with a hack because scrollViewDidEndScrollingAnimation is not useful either.

The case of a PAGINATED scroll view:

Because, I guess, Apple apply an acceleration curve, scrollViewDidEndDecelerating get called for every drag so there's no need to use scrollViewDidEndDragging in this case.


I think scrollViewDidEndDecelerating is the one you want. Its UIScrollViewDelegates optional method:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Tells the delegate that the scroll view has ended decelerating the scrolling movement.

UIScrollViewDelegate documentation