如何检测UIScrollView何时完成滚动

UIScrollViewDelegate有两个委托方法scrollViewDidScroll:scrollViewDidEndScrollingAnimation:但是这些都不能告诉你什么时候滚动完成。 scrollViewDidScroll只会通知你,滚动视图没有滚动,而是滚动。

另一种方法scrollViewDidEndScrollingAnimation似乎只有在用户滚动的情况下以编程方式移动滚动视图时才会触发。

有没有人知道scheme来检测滚动视图完成滚动时?

你正在寻找的方法是scrollViewDidEndDragging:willDecelerate:scrollViewDidEndDecelerating: 第一个在用户抬起手指后总是被调用。 如果他们滚动得足够快,导致减速, willDecelerate将是YES ,第二种方法将在减速完成后调用。

( 来自UIScrollViewDelegate文档 )

320的实现是更好的 – 这是一个补丁,以获得一致的开始/结束滚动。

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

我认为scrollViewDidEndDecelerating是你想要的。 它的UIScrollViewDelegates可选方法:

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

告诉委托人,滚动视图已经结束,减慢了滚动的速度。

UIScrollViewDelegate文档

对于与拖动交互相关的所有滚动,这将是足够的:

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { _isScrolling = NO; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { _isScrolling = NO; } } 

现在,如果你的滚动是由于一个编程的setContentOffset / scrollRectVisible(与animated = YES,或者你显然知道什么时候滚动结束):

  - (void)scrollViewDidEndScrollingAnimation { _isScrolling = NO; } 

如果你的滚动是由于别的东西(如键盘打开或键盘closures),似乎你必须检测事件与黑客,因为scrollViewDidEndScrollingAnimation也没有用。

PAGINATED滚动视图的情况:

因为,我猜,苹果应用加速度曲线, scrollViewDidEndDecelerating调用每个拖动,所以在这种情况下不需要使用scrollViewDidEndDragging

这已经在其他一些答案中描述过,但是这里(在代码中)如何结合scrollViewDidEndDeceleratingscrollViewDidEndDragging:willDecelerate在滚动完成时执行一些操作:

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self stoppedScrolling]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { [self stoppedScrolling]; } } - (void)stoppedScrolling { // done, do whatever } 

我只是刚刚发现这个问题,这几乎是相同的我问: 如何确切地知道什么时候UIScrollView的滚动已停止?

虽然在滚动时做了EndEndDecelerating工作,但是用固定版本的平移不会注册。

我最终find了解决scheme。 didEndDragging有一个参数WillDecelerate,在静态释放情况下是false。

通过在DidEndDragging中检查!decelerate,并结合didEndDecelerating,您将得到滚动结束的两种情况。

我已经试过阿什利·斯玛特的答案,它的工作就像一个魅力。 这是另一个想法,只使用scrollViewDidScroll

 -(void)scrollViewDidScroll:(UIScrollView *)sender { if(self.scrollView_Result.contentOffset.x == self.scrollView_Result.frame.size.width) { // You have reached page 1 } } 

我只有两页,所以它为我工作。 但是,如果您有多个页面,则可能会出现问题(您可以检查当前的偏移量是否是宽度的倍数,但是您不知道用户是否停在第2页或正在前往第3页或更多)

Swift版本的接受答案:

 func scrollViewDidScroll(scrollView: UIScrollView) { // example code } func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { // example code } func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView!, atScale scale: CGFloat) { // example code } 

刚刚开发的解决scheme来检测滚动完成应用程序的范围: https : //gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

这是基于追踪运行模式变化的思想。 滚动后至less0.2秒后执行块。

这是追踪runloop模式更改的核心思想iOS10 +:

 - (void)tick { [[NSRunLoop mainRunLoop] performInModes:@[ UITrackingRunLoopMode ] block:^{ [self tock]; }]; } - (void)tock { self.runLoopModeWasUITrackingAgain = YES; [[NSRunLoop mainRunLoop] performInModes:@[ NSDefaultRunLoopMode ] block:^{ [self tick]; }]; } 

针对iOS2 +等低部署目标的解决scheme:

 - (void)tick { [[NSRunLoop mainRunLoop] performSelector:@selector(tock) target:self argument:nil order:0 modes:@[ UITrackingRunLoopMode ]]; } - (void)tock { self.runLoopModeWasUITrackingAgain = YES; [[NSRunLoop mainRunLoop] performSelector:@selector(tick) target:self argument:nil order:0 modes:@[ NSDefaultRunLoopMode ]]; } 

回顾(和新手)。 这并不痛苦。 只需添加协议,然后添加您需要检测的function。

在包含UIScrolView的视图(类)中,添加协议,然后从这里添加任何function到您的视图(类)。

 // -------------------------------- // In the "h" file: // -------------------------------- @interface myViewClass : UIViewController <UIScrollViewDelegate> // <-- Adding the protocol here // Scroll view @property (nonatomic, retain) UIScrollView *myScrollView; @property (nonatomic, assign) BOOL isScrolling; // Protocol functions - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView; // -------------------------------- // In the "m" file: // -------------------------------- @implementation BlockerViewController - (void)viewDidLoad { CGRect scrollRect = self.view.frame; // Same size as this view self.myScrollView = [[UIScrollView alloc] initWithFrame:scrollRect]; self.myScrollView.delegate = self; self.myScrollView.contentSize = CGSizeMake(scrollRect.size.width, scrollRect.size.height); self.myScrollView.contentInset = UIEdgeInsetsMake(0.0,22.0,0.0,22.0); // Allow dragging button to display outside the boundaries self.myScrollView.clipsToBounds = NO; // Prevent buttons from activating scroller: self.myScrollView.canCancelContentTouches = NO; self.myScrollView.delaysContentTouches = NO; [self.myScrollView setBackgroundColor:[UIColor darkGrayColor]]; [self.view addSubview:self.myScrollView]; // Add stuff to scrollview UIImage *myImage = [UIImage imageNamed:@"foo.png"]; [self.myScrollView addSubview:myImage]; } // Protocol functions - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { NSLog(@"start drag"); _isScrolling = YES; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { NSLog(@"end decel"); _isScrolling = NO; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { NSLog(@"end dragging"); if (!decelerate) { _isScrolling = NO; } } // All of the available functions are here: // https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html 

我有一个点击和拖动动作的情况下,我发现拖动调用scrollViewDidEndDecelerating

并且用代码手动更改偏移量([_scrollView setContentOffset:contentOffset animated:YES];)正在调用scrollViewDidEndScrollingAnimation。

 //This delegate method is called when the dragging scrolling happens, but no when the tapping - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { //do whatever you want to happen when the scroll is done } //This delegate method is called when the tapping scrolling happens, but no when the dragging -(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { //do whatever you want to happen when the scroll is done } 

如果有人需要,这里是斯威夫特的Ashley Smart回答

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

UIScrollview有一个委托方法

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

在委托方法中添加下面的代码行

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { CGSize scrollview_content=scrollView.contentSize; CGPoint scrollview_offset=scrollView.contentOffset; CGFloat size=scrollview_content.width; CGFloat x=scrollview_offset.x; if ((size-self.view.frame.size.width)==x) { //You have reached last page } } 

有一个UIScrollViewDelegate的方法可以用来检测(或者更好地说'预测')当滚动真的完成时:

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

UIScrollViewDelegate可以用来检测(或更好地说'预测'),当滚动真的完成。

在我的情况下,我使用水平滚动如下(在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 } 

另一种方法是使用scrollViewWillEndDragging:withVelocity:targetContentOffset ,只要用户抬起手指,并包含滚动将停止的目标内容偏移,就会调用它。 在scrollViewDidScroll:使用此内容偏移scrollViewDidScroll:正确标识滚动视图停止滚动的时间。

 private var targetY: CGFloat? public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { targetY = targetContentOffset.pointee.y } public func scrollViewDidScroll(_ scrollView: UIScrollView) { if (scrollView.contentOffset.y == targetY) { print("finished scrolling") }