Как обнаружить, когда UIScrollView завершил прокрутку

UIScrollViewDelegate имеет два метода делегирования scrollViewDidScroll: и scrollViewDidEndScrollingAnimation: но ни один из них не говорит вам о завершении прокрутки. scrollViewDidScroll уведомляет вас только о том, что в прокрутке отображается прокрутка, а не завершена прокрутка.

Другой метод scrollViewDidEndScrollingAnimation только срабатывает, если вы программно перемещаете представление прокрутки, а не если пользователь прокручивает.

Кто-нибудь знает схему, чтобы обнаружить, когда прокрутка просмотрела завершение прокрутки?

Методы, которые вы ищете, – 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]; ... } 

Я думаю, что scrollViewDideEndDecelerating – это тот, который вы хотите. Его необязательный метод UIScrollViewDelegates:

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

Сообщает делегату, что просмотр прокрутки завершил замедление движения прокрутки.

Документация UIScrollViewDelegate

Для всех свитков, связанных с drag and dropм взаимодействий, этого будет достаточно:

 - (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; } 

Если ваш свиток связан с чем-то другим (например, с открытием клавиатуры или закрытием клавиатуры), похоже, вам придется обнаруживать событие с помощью взлома, потому что scrollViewDidEndScrollingAnimation не является полезным.

Случай просмотра прокрутки PAGINATED :

Поскольку, я думаю, Apple применяет кривую ускорения, scrollViewDidEndDecelerating для каждого перетаскивания, поэтому в этом случае нет необходимости использовать scrollViewDidEndDragging .

Это описано в некоторых других ответах, но вот (в коде), как объединить scrollViewDidEndDecelerating и scrollViewDidEndDragging:willDecelerate чтобы выполнить некоторую операцию при завершении прокрутки:

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

Я только что нашел этот вопрос, который почти такой же, как я спросил: « Как точно знать, когда прокрутка UIScrollView прекратилась?

Хотя didEndDecelerating работает при прокрутке, панорамирование со стационарным выпуском не регистрируется.

В итоге я нашел решение. У didEndDragging есть параметр WillDecelerate, который является ложным в ситуации стационарного выпуска.

Проверяя для замедления в DidEndDragging, в сочетании с 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-му или Больше)

Быстрая версия принятого ответа:

 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 } 

Только что разработанное решение для обнаружения при прокрутке готового приложения: https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

Он основан на идее отслеживания изменений режимов runloop. И выполнять блоки по крайней мере через 0,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 +:

 - (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 ]]; } 

Напомним (и для новичков). Это не так больно. Просто добавьте протокол, затем добавьте функции, необходимые для обнаружения.

В представлении (classе), который содержит UIScrolView, добавьте протокол, затем добавьте любые функции отсюда к вашему представлению (classу).

 // -------------------------------- // In the "h" file: // -------------------------------- @interface myViewClass : UIViewController  // <-- 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 

У меня был случай нажатия и перетаскивания, и я узнал, что drag and drop вызывало scrollViewDidEndDecelerating

И изменение смещения вручную с кодом ([_scrollView setContentOffset: contentOffset анимированный: 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 } 

Если кому-то нужно, вот ответ Эшли Smart в 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) ... } 

Альтернативой было бы использовать scrollViewWillEndDragging:withVelocity:targetContentOffset который вызывается всякий раз, когда пользователь поднимает палец и содержит смещение целевого содержимого, где прокрутка останавливается. Использование этого смещения содержимого в scrollViewDidScroll: правильно определяет, когда прокрутка просмотрела прекращение прокрутки.

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

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) 

UIScrollViewDelegate который может использоваться для обнаружения (или, лучше сказать, «предсказать»), когда прокрутка действительно завершена.

В моем случае я использовал его с горизонтальной прокруткой следующим образом (в Swift 3 ):

 func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { perform(#selector(self.actionOnFinishedScrolling), with: nil, afterDelay: Double(velocity.x)) } func actionOnFinishedScrolling() { print("scrolling is finished") // do what you need } 
  • UIScrollView не прокручивается после обновления до iOS7 / xcode 5
  • UIScrollview с UIButtons - как воссоздать трамплин?
  • Показать / скрыть UIToolbar, «перемещать пальцы», точно так же, как, например, в iOS7 Safari
  • Как заблокировать горизонтальную прокрутку scrollView в iOS
  • Прокрутка двумя пальцами с помощью UIScrollView
  • Как настроить размер содержимого UIScrollView динамически
  • Прокрутка Не работает IOS 7
  • Есть ли хорошие полезные учебные пособия UIScrollView в сети?
  • Интерфейс UIScrollViewBegan
  • Проверка UIScrollView iPhone
  • UIScrollView Бесконечная прокрутка
  • Давайте будем гением компьютера.