65

I'm using a UICollectionView to scroll through a set of thumbnails quickly. Once scrolling ends, I'd like to display a larger hi-res version of the current thumbnail.

How can I detect when the user has completed scrolling? I do implement didEndDisplayingCell, but that only tells me when a particular cell has scrolled off; it doesn't tell me when the scroll motion actually completes.

iDev
  • 23,142
  • 6
  • 58
  • 85
George Armhold
  • 29,784
  • 45
  • 147
  • 224

6 Answers6

128
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionView : UIScrollView

UICollectionView is a subclass of UIScrollView. So if you have set the delegate and implemented UIScrollViewDelegate, you should be able to detect this the same way as UIScrollView.

For eg:-

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;

As per documentation, the above method should tell when the scroll view has ended decelerating the scrolling movement.

iDev
  • 23,142
  • 6
  • 58
  • 85
  • 6
    Brilliant. You don't know how much nasty code this saved me. I was trying to monitor it using the shouldInvalidateLayoutForBoundsChange callback in my UICollectionViewFlowLayout. What a n00b. – Kyle Clegg May 11 '13 at 21:54
  • 2
    Please see D6mi's note below about scrollViewDidEndScrollingAnimation. scrollViewDidEndDecelerating does not get called for programmatic scrolling. – RajV Oct 24 '16 at 11:42
  • 8
    Indeed, I don't understand why the current answer is the accepted one, it does not work `scrollViewDidEndDecelerating ` is not called but like D6mi said, `scrollViewDidEndScrollingAnimation` is called – AkademiksQc Dec 16 '16 at 15:03
  • This answer is not complete. – Nathanael Jun 28 '17 at 22:54
  • Brilliant answer. @Nathanael, the answer is complete since he pointed out that UICollectionView is a subclass of UIScrollView and also quoted an eg:- delegate method. Rest of the answers just took this answer and added other delegate methods to it. Ideally rest should be just a comment on this answer. The keypart is collectionview is just a scrollview, rest can be figured out. – adev Jun 30 '17 at 07:04
60

Just to cover your bases you should implement both these UIScrollViewDelegate methods. In some cases there may not be a deceleration (and scrollViewDidEndDecelerating would not be called), for e.g., the page is fully scrolled in place. In those case do your update right there in scrollViewDidEndDragging.

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

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
  [self updateStuff];
}
Abey M
  • 719
  • 6
  • 8
  • 7
    Thank you for sharing this! This should be the accepted answer b/c OP asked "scrolling has stopped" -- without both methods in place the user can easily scroll the collection view down and release finger without flicking and `scrollViewDidEndDecelerating` will NOT get called, `scrollViewDidEndDragging` does. Thank you for posting this answer. – John Erck Jul 18 '14 at 21:34
35

An important fact to note here.

This method gets called on User initiated scrolls (i.e a Pan gesture):

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;

or in Swift:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)


On the other hand, this one gets called on all manually (programatically) initiated scrolls (like scrollRectToVisible or scrollToItemAtIndexPath):

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView

or in Swift:

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)

D6mi
  • 553
  • 6
  • 15
9

Swift 3 version:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    // Your code here
}
Mr Stanev
  • 1,512
  • 1
  • 16
  • 22
8

Swift 3 version of Abey M and D6mi 's answers:

When scroll is caused by user action

public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if (!decelerate) {
        //cause by user
        print("SCROLL scrollViewDidEndDragging")
    }
}

public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    //caused by user
    print("SCROLL scrollViewDidEndDecelerating")
}

When scroll is caused by code action (programmatically): (like "scrollRectToVisible" or "scrollToItemAtIndexPath")

public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    //caused by code
    print("SCROLL scrollViewDidEndScrollingAnimation")
}

Notes:

  • Put these functions in your UIScrollViewDelegate or UICollectionViewDelegate delegate.
  • if you don't have a separate delegate, make your current class extend a UIScrollViewDelegate op top of your class file

.

open class MyClass: NSObject , UICollectionViewDelegate

and somewhere in your viewWillAppear make the class its own delegate

override open func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // ...
    self.myScrollView.delegate = self
    // ...
}
drpawelo
  • 1,655
  • 17
  • 12
0

if you want to use the visible indexpath:

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


    if([self.collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionHeader]){
        NSIndexPath *firstVisibleIndexPath = [[self.collectionView indexPathsForVisibleSupplementaryElementsOfKind:UICollectionElementKindSectionHeader] firstObject];
        [self.collectionView scrollToItemAtIndexPath:firstVisibleIndexPath atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
    }
}
Ofir Malachi
  • 842
  • 9
  • 16