How to detect when a UIScrollView has finished scrolling In Swift

25,017

Solution 1

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")
}

Solution 2

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
}

Solution 3

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
}

Solution 4

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
}
Share:
25,017
Don Hall
Author by

Don Hall

Updated on November 25, 2021

Comments

  • Don Hall
    Don Hall over 2 years

    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?

  • Bogdan
    Bogdan about 7 years
    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
    Bogdan about 7 years
    And even scrollViewDidEndScrollingAnimation, I didn't considered it earlier too :)
  • AmiguelS
    AmiguelS over 6 years
    What is the diference between checking isDecelerating vs checking the param decelerate?