12

There was, by all accounts, an excellent solution to this problem in Obj-C presented by Ashley Smart (How to detect when a UIScrollView has finished scrolling).

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

...
}

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

I need a solution, however, in Swift.

It appears that the excellent delay function, contributed by Matt (dispatch_after - GCD in swift?) is likely to help.

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

and implemented as ...

delay(0.4) {
    // do stuff
}

but I've still not put it together. Any help?

Community
  • 1
  • 1
Don Hall
  • 125
  • 1
  • 1
  • 5

5 Answers5

29

The delegate method tells you when finished

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    self.stoppedScrolling()
}

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        self.stoppedScrolling()
    }
}

func stoppedScrolling() {
    println("Scroll finished")
}
Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
Nekak Kinich
  • 865
  • 7
  • 9
  • 2
    This doesn't work in case the scroll has no deceleration towards the stop point, which may be very well when the user lifts his finger, so you may consider scrollViewDidEndDragging as well. – Bogdan Feb 23 '17 at 08:33
  • And even scrollViewDidEndScrollingAnimation, I didn't considered it earlier too :) – Bogdan Feb 24 '17 at 11:07
10

The scrollViewDidEndDecelerating won't be called if user is scrolling slowly. Here's Ashley Smart asnwear in Swift

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    NSObject.cancelPreviousPerformRequests(withTarget: self)
    perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation), with: nil, afterDelay: 0.3)
}

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    NSObject.cancelPreviousPerformRequests(withTarget: self)
// Call your function here
}
Xernox
  • 1,587
  • 1
  • 19
  • 32
5

There is a method of UIScrollViewDelegate which can be used to detect (or better to say 'predict') when scrolling has really finished:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)

of UIScrollViewDelegate which can be used to detect (or better to say 'predict') when scrolling has really finished.

In my case I used it with horizontal scrolling as following (in Swift 3):

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    perform(#selector(self.actionOnFinishedScrolling), with: nil, afterDelay: Double(velocity.x))
}
func actionOnFinishedScrolling() {
    print("scrolling is finished")
    // do what you need
}
lexpenz
  • 51
  • 1
  • 3
1

You need to check whether the user has stopped dragging and if the view is still decelerating after the user stopped dragging:

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if collectionView.isDecelerating == false {
        // Perform whichever function you desire for when scrolling has stopped
    }
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    // Perform whichever function you desire for when scrolling has stopped
}
Akhrameev
  • 306
  • 4
  • 7
Ever Uribe
  • 399
  • 3
  • 12
0

Swift Version:

NOTE: The answer you mentioned is a possible solution and differ with cases

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        // YOUR CODE........
        NSObject.cancelPreviousPerformRequests(withTarget: self)
        self.perform(#selector(scrollViewDidEndScrollingAnimation(_:)), with: scrollView, afterDelay: 0.3)
    }

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {


        // YOUR CODE ........
        NSObject.cancelPreviousPerformRequests(withTarget: self)
    }
Abhishek Thapliyal
  • 2,682
  • 3
  • 22
  • 55