In short, I need to know exactly when the scrollview stopped scrolling. By \'stopped scrolling\', I mean the moment at which it is no longer moving and not being touched.
Delegate methods mentioned in this post answers did not help me. I found another answer which detect final scroll end in scrollViewDidScroll:
Link is here
I answered this question on my blog which outlines how to do it without problems.
It involves 'intercepting the delegate' to do a few things, then to pass the delegate messages to where they were intended. This is required because there are a few scenarios where the delegate fires if it moved programmatically, or not, or both, and so anyway, best to just look at this link below:
How to know when a UIScrollView is scrolling
It's a little tricky, but I've put up a recipe there and have explained the parts in detail.
It is important to understand, that the when UIScrollView is stopping to move it triggers two different functions of the UIScrollViewDelegate depending on how strong the user has pushed.
So in conclusion it is necessary to combine the two functions like @WayneBurkett already pointed out in his answer.
In Swift 5.X:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
myFunction()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if decelerate == false {
myFunction()
}
}
private func myFunction() {
// scrollView did stop moving -> do what ever
// ...
}
[Edited Answer] This is what I use - it handles all the edge cases. You need an ivar to keep state, and as shown in the comments, there are other ways to handle this.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//[super scrollViewWillBeginDragging:scrollView]; // pull to refresh
self.isScrolling = YES;
NSLog(@"+scrollViewWillBeginDragging");
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
//[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; // pull to refresh
if(!decelerate) {
self.isScrolling = NO;
}
NSLog(@"%@scrollViewDidEndDragging", self.isScrolling ? @"" : @"-");
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidEndDecelerating");
}
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidScrollToTop");
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidEndScrollingAnimation");
}
I created a really simple project uses the above code, so that when a person interacts with a scrollView (including a WebView), it inhibits process intensive work until the user stops interacting with the scrollView AND the scrollview has stopped scrolling. It's like 50 lines of code: ScrollWatcher
Here's 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
}
I found a solution:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
I did not notice that last bit before, willDecelerate
. It is false when the scrollView is stationary when ending the touch. Combined with the above-mentioned speed check, I can snap both when it's slow (and not being touched) or when it's stationary.
For anyone not doing any snapping but needs to know when scrolling stopped, didEndDecelerating
will be called at the end of the scroll movement. Combined with a check on willDecelerate
in didEndDragging
, you'll know when the scrolling has stopped.