Как определить, когда UIScrollView завершил прокрутку - PullRequest
136 голосов
/ 14 июня 2009

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

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

Кто-нибудь знает схему обнаружения прокрутки в представлении прокрутки?

Ответы [ 17 ]

166 голосов
/ 07 декабря 2009

Реализации 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];
...
}
144 голосов
/ 14 июня 2009
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self stoppedScrolling];
}

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

- (void)stoppedScrolling {
    // ...
}
21 голосов
/ 14 июня 2009

Я думаю, что scrollViewDidEndDecelerating - это то, что вам нужно. Его UIScrollViewDelegates необязательный метод:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

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

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

20 голосов
/ 16 мая 2013

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

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    _isScrolling = NO;
}

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

Теперь, если ваша прокрутка вызвана программным setContentOffset / scrollRectVisible (с animated = ДА или вы, очевидно, знаете, когда прокрутка закончена):

 - (void)scrollViewDidEndScrollingAnimation {
     _isScrolling = NO;
}

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

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

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

19 голосов
/ 10 января 2014

Это было описано в некоторых других ответах, но вот (в коде), как объединить 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
}
7 голосов
/ 11 октября 2011

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

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

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

Проверяя! Замедление в DidEndDragging в сочетании с didEndDecelerating, вы получаете обе ситуации, которые заканчивают прокрутку.

3 голосов
/ 18 декабря 2018
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    scrollingFinished(scrollView: scrollView)
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if decelerate {
        //didEndDecelerating will be called for sure
        return
    }
    else {
        scrollingFinished(scrollView: scrollView)
    }
}

func scrollingFinished(scrollView: UIScrollView) {
   // Your code
}
3 голосов
/ 06 августа 2015

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

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
}
3 голосов
/ 23 сентября 2011

Я попробовал ответ Эшли Смарт, и он сработал как шарм. Вот еще одна идея, с использованием только scrollViewDidScroll

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

У меня просто было две страницы, так что у меня получилось. Однако, если у вас более одной страницы, это может быть проблематично (вы можете проверить, кратно ли текущее смещение ширине, но тогда вы не узнаете, остановился ли пользователь на 2-й странице или находится на пути к 3-й или больше)

2 голосов
/ 10 марта 2017

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

Он основан на идее отслеживания изменений режимов runloop. И выполнять блоки по крайней мере через 0,2 секунды после прокрутки.

Это основная идея для отслеживания изменений режимов запуска 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 ]];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...