UICollectionView:如何检测滚动已停止?

71

我正在使用一个 UICollectionView 来快速浏览一组缩略图。当滚动结束后,我想显示当前缩略图的更大分辨率版本。

如何检测用户何时完成滚动?我确实实现了 didEndDisplayingCell,但这只告诉我特定单元格已经滚动出去了;它并没有告诉我滚动操作实际上何时完成。

7个回答

137
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionView : UIScrollView

UICollectionViewUIScrollView的子类。因此,如果您已设置委托并实现了UIScrollViewDelegate,则应该能够以与UIScrollView相同的方式检测到它。

例如:

Objective-C:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

Swift:

func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView)
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)

根据文档,上述方法应该能够告诉滚动视图何时结束减速滚动。请注意,正如评论中所提到的那样,scrollViewDidEndDecelerating在某些情况下可能不会被调用。

7
太棒了!你不知道这个救了我多少烦人的代码。我曾试图使用 shouldInvalidateLayoutForBoundsChange 回调函数来监控我的 UICollectionViewFlowLayout,简直是个“新手”。 - Kyle Clegg
2
请查看D6mi关于scrollViewDidEndScrollingAnimation的注释。对于编程滚动,scrollViewDidEndDecelerating不会被调用。 - RajV
9
确实,我不明白为什么目前被接受的答案是这个,它并不起作用。scrollViewDidEndDecelerating没有被调用,但像D6mi所说,scrollViewDidEndScrollingAnimation被调用了。 - AkademiksQc
这个答案不完整。 - Nathanael
很棒的答案。@Nathanael,答案已经很完整了,因为他指出了UICollectionView是UIScrollView的子类,并引用了一个例子:代理方法。其余的答案只是在这个答案的基础上添加了其他代理方法。理想情况下,其余的答案应该只是对这个答案的评论。关键部分是collectionview只是一个scrollview,其余的可以自己想出来。 - adev

61

为了防患未然,您应该实现这两个 UIScrollViewDelegate 方法。在某些情况下,可能不会有减速(并且 scrollViewDidEndDecelerating 不会被调用),例如,页面完全滚动到位的情况。在这种情况下,请直接在 scrollViewDidEndDragging 中进行更新。

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

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

7
谢谢分享!这应该是被采纳的答案,因为提问者问到“滚动已停止”-- 如果没有这两种方法,用户可以很容易地将集合视图向下滚动并释放手指而不进行轻扫操作,此时scrollViewDidEndDecelerating不会被调用,而是调用了scrollViewDidEndDragging。感谢您发布这个答案。 - John Erck

36

需要注意的重要事实。

该方法在用户发起滚动(即平移手势)时调用:

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

或者在Swift中:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)


另一方面,这个方法在所有手动(编程方式)发起的滚动上被调用(如scrollRectToVisiblescrollToItemAtIndexPath):

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView

或者在Swift中:

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)


14

Abey M和D6mi的答案的Swift 3版本:

当滚动是由用户操作引起时

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

当滚动是由代码动作(编程方式)引起时:(例如“scrollRectToVisible”或“scrollToItemAtIndexPath”)

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

备注:

  • 将这些函数放在您的UIScrollViewDelegate或UICollectionViewDelegate代理中。
  • 如果您没有单独的代理,请使您当前的类在类文件顶部扩展一个UIScrollViewDelegate。

.


备注:

  • 将这些函数放在您的UIScrollViewDelegate或UICollectionViewDelegate代理中。
  • 如果您没有单独的代理,请使您当前的类在类文件顶部扩展一个UIScrollViewDelegate。

.

open class MyClass: NSObject , UICollectionViewDelegate

在您的viewWillAppear中,让该类成为其自己的代理

override open func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // ...
    self.myScrollView.delegate = self
    // ...
}

9

Swift 3版本:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    // Your code here
}

0

如果你想使用可见的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];
    }
}

0
获取你的集合视图索引,并不要忘记在你的类中导入UIScrollViewDelegate
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let xPoint = scrollView.contentOffset.x + scrollView.frame.width / 2
    let yPoint = scrollView.frame.height / 2
    let center = CGPoint(x: xPoint, y: yPoint)
    if let ip = self.collectionView.indexPathForItem(at: center) {
        pageIndex = ip.row
    }
    print(">>>>>>>>>\(pageIndex)")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接