164

UIScrollViewDelegate has got two delegate methods scrollViewDidScroll: and scrollViewDidEndScrollingAnimation: but neither of these tell you when scrolling has completed. scrollViewDidScroll only notifies you that the scroll view did scroll not that it has finished scrolling.

The other method scrollViewDidEndScrollingAnimation only seems to fire if you programmatically move the scroll view not if the user scrolls.

Does anyone know of scheme to detect when a scroll view has completed scrolling?

4

20 に答える 20

181

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];
...
}
于 2009-12-07T00:38:35.113 に答える
171
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self stoppedScrolling];
}

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

- (void)stoppedScrolling {
    // ...
}
于 2009-06-14T17:44:17.523 に答える
21

ドラッグ操作に関連するすべてのスクロールでは、これで十分です。

- (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、この場合は使用する必要はありません。

于 2013-05-16T08:06:23.037 に答える
21

これは他のいくつかの回答で説明されていますが、スクロールが終了したときにいくつかの操作を組み合わせて実行する方法を(コードで)次に示しますscrollViewDidEndDeceleratingscrollViewDidEndDragging:willDecelerate

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

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

- (void)stoppedScrolling
{
    // done, do whatever
}
于 2014-01-10T17:34:38.050 に答える
20

scrollViewDidEndDecelerating はあなたが望むものだと思います。その UIScrollViewDelegates オプション メソッド:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

スクロール ビューが終了したことをデリゲートに伝え、スクロールの動きを減速します。

UIScrollViewDelegate のドキュメント

于 2009-06-14T17:44:28.553 に答える
8

I only just found this question, which is pretty much the same I asked: How to know exactly when a UIScrollView's scrolling has stopped?

Though didEndDecelerating works when scrolling, panning with stationary release does not register.

I eventually found a solution. didEndDragging has a parameter WillDecelerate, which is false in the stationary release situation.

By checking for !decelerate in DidEndDragging, combined with didEndDecelerating, you get both situations that are the end of scrolling.

于 2011-10-11T12:15:51.870 に答える
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
}
于 2015-08-06T14:44:43.537 に答える
3

アプリ全体でスクロールが終了したことを検出するソリューションを開発しました: https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

これは、ランループ モードの変更を追跡するという考えに基づいています。スクロール後、少なくとも 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 ]];
}
于 2017-03-09T21:25:30.500 に答える
3

私はアシュリー・スマートの答えを試してみましたが、それは魅力的でした。scrollViewDidScroll のみを使用した別のアイデアを次に示します。

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

2ページしかなかったので、うまくいきました。ただし、複数のページがある場合は、問題が発生する可能性があります (現在のオフセットが幅の倍数であるかどうかを確認できますが、ユーザーが 2 ページ目で停止したのか、3 ページ目に向かっているのか、またはもっと)

于 2011-09-23T14:00:52.453 に答える
1

要約すると(および初心者向け)。そんなに痛くないです。プロトコルを追加してから、検出に必要な機能を追加するだけです。

UIScrollView を含むビュー (クラス) で、プロトコルを追加し、ここからの関数をビュー (クラス) に追加します。

// --------------------------------
// 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
于 2013-11-16T11:04:32.680 に答える
1

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
}
于 2017-03-10T11:37:21.977 に答える
1

アクションをタップしてドラッグするケースがあり、ドラッグが scrollViewDidEndDecelerating を呼び出していることがわかりました

そして、コード ([_scrollView setContentOffset:contentOffset animation: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
}
于 2014-04-04T17:49:32.787 に答える