Jak wykryć, że uiscrollview zakończył przewijanie

UIScrollViewDelegate ma dwie metody delegowania scrollViewDidScroll: i scrollViewDidEndScrollingAnimation:, ale żadna z nich nie informuje cię po zakończeniu przewijania. scrollViewDidScroll informuje tylko, że widok przewijania się przewijał, a nie, że zakończył przewijanie.

Druga metoda scrollViewDidEndScrollingAnimation wydaje się uruchamiać tylko wtedy, gdy programowo przesuwasz widok przewijania, a nie gdy użytkownik przewija.

Czy ktoś zna schemat wykrywania, kiedy widok przewijania zakończył przewijanie?

Author: Michael Gaylord, 2009-06-14

15 answers

Metody, których szukasz to scrollViewDidEndDragging:willDecelerate: i scrollViewDidEndDecelerating:. Pierwszy jest zawsze wywoływany po tym, jak użytkownik podniesie palec. Jeśli przewijały się wystarczająco szybko, aby spowodować opóźnienie, willDecelerate będzie YES, a druga metoda zostanie wywołana po zakończeniu opóźnienia.

(z UIScrollViewDelegate docs.)

 136
Author: Suvesh Pratapa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-15 07:33:17

Implementacje 320 są o wiele lepsze - oto patch, aby uzyskać spójny początek/koniec zwoju.

-(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];
...
}
 161
Author: Ashley Smart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-28 15:27:31

Myślę, że scrollViewDidEndDecelerating jest tym, którego chcesz. Uiscrollviewdelegates opcjonalną metodę:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Informuje delegata, że widok przewijania zakończył się opóźnieniem Ruchu Przewijania.

Uiscrollviewdelegate documentation

 21
Author: texmex5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-14 17:44:28

Dla wszystkich zwojów związanych z interakcjami przeciągania wystarczy:

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

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

Teraz, jeśli twój scroll jest spowodowany programowym setContentOffset/scrollRectVisible (z animated = YES lub oczywiście wiesz, kiedy scroll jest zakończony):

 - (void)scrollViewDidEndScrollingAnimation {
     _isScrolling = NO;
}

Jeśli twój scroll jest spowodowany czymś innym (jak otwieranie lub zamykanie klawiatury), wygląda na to, że będziesz musiał wykryć Zdarzenie za pomocą hacka, ponieważ scrollViewDidEndScrollingAnimation również nie jest przydatna.

Przypadek PAGINOWANY zwój Widok:

Ponieważ, jak sądzę, Apple stosuje krzywą przyspieszenia, scrollViewDidEndDecelerating są wywoływane dla każdego przeciągnięcia, więc nie ma potrzeby używania scrollViewDidEndDragging w tym przypadku.

 19
Author: Aurelien Porte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-23 23:09:59

Zostało to opisane w niektórych innych odpowiedziach, ale oto (w kodzie) jak połączyć scrollViewDidEndDecelerating i scrollViewDidEndDragging:willDecelerate, aby wykonać jakąś operację po zakończeniu przewijania:

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

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

- (void)stoppedScrolling
{
    // done, do whatever
}
 17
Author: Wayne Burkett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-10 17:34:38

Właśnie znalazłem to pytanie, które jest prawie takie samo, jakie zadałem: Jak dokładnie wiedzieć, kiedy przewijanie UIScrollView zostało zatrzymane?

Chociaż didEndDecelerating działa podczas przewijania, panoramowanie z zwolnieniem stacjonarnym nie jest rejestrowane.

W końcu znalazłem rozwiązanie. didEndDragging ma parametr WillDecelerate, który jest fałszywy w sytuacji zwolnienia stacjonarnego.

Sprawdzając !spowolnienie w Didenddraging, w połączeniu z didEndDecelerating, otrzymujesz obie sytuacje, które są końcem przewijania.

 7
Author: Aberrant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:43

Próbowałem odpowiedzi Ashley Smart i zadziałało jak urok. Oto inny pomysł, z użyciem tylko scrollViewDidScroll

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

Miałem tylko dwie strony, więc mi się udało. Jeśli jednak masz więcej niż jedną stronę, może to być problematyczne (możesz sprawdzić, czy bieżące przesunięcie jest wielokrotnością szerokości, ale nie wiesz, czy użytkownik zatrzymał się na 2 stronie lub jest w drodze do 3 lub więcej)

 3
Author: Ege Akpinar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-23 14:00:52

Wersja Swift zaakceptowanej odpowiedzi:

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
Author: zzzz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-06 14:44:43

Właśnie opracowano rozwiązanie do wykrywania podczas przewijania ukończonych aplikacji: https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

Opiera się na idei śledzenia zmian trybów runloop. I wykonywać bloki co najmniej po 0,2 sekundy po przewinięciu.

Jest to podstawowy pomysł na śledzenie zmian trybów runloop iOS10+:

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

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

I rozwiązanie dla małych celów wdrożeniowych, takich jak 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 ]];
}
 2
Author: k06a,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 10:57:54

Do podsumowania (i dla początkujących). To nie jest aż tak bolesne. Wystarczy dodać protokół, a następnie dodać funkcje potrzebne do wykrywania.

W widoku (class), który zawiera UIScrolView, dodaj protokół, a następnie dodaj dowolne funkcje z tego miejsca do widoku (class).

// --------------------------------
// 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
 1
Author: bob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-16 11:04:32

Miałem przypadek stukania i przeciągania akcji i dowiedziałem się, że przeciąganie wywoływało scrollViewDidEndDecelerating

I przesunięcie zmiany ręcznie kodem ([_scrollView setContentOffset:contentOffset animated:YES];) wywoływało 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
}
 1
Author: Adriana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-04 17:49:32

Jeśli ktoś potrzebuje, oto Ashley Smart answer W 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)
    ...
}
 1
Author: Xernox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-02 16:22:24

Alternatywą byłoby użycie scrollViewWillEndDragging:withVelocity:targetContentOffset, które jest wywoływane za każdym razem, gdy użytkownik podniesie palec i zawiera przesunięcie docelowej zawartości, w którym zatrzyma się przewijanie. Użycie tego przesunięcia zawartości w scrollViewDidScroll: poprawnie określa, kiedy widok przewijania przestał przewijać.

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")
    }
 1
Author: Wonder Dog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-26 11:33:27

UIScrollview ma metodę delegata

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Dodaj poniższe linie kodu w metodzie delegate

- (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
}
}
 0
Author: Rahul K Rajan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-19 06:25:15

Istnieje metoda UIScrollViewDelegate, która może być użyta do wykrycia (lub lepiej powiedzieć "przewidzieć"), gdy przewijanie naprawdę się skończy:

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

Z UIScrollViewDelegate, które mogą być używane do wykrywania (lub lepiej powiedzieć "przewijania"), gdy przewijanie jest naprawdę zakończone.

W moim przypadku używałem go z poziomym przewijaniem w następujący sposób (w 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
}
 0
Author: lexpenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 11:44:34