如何准确知道 UIScrollView 的滚动何时停止?

How to know exactly when a UIScrollView#39;s scrolling has stopped?(如何准确知道 UIScrollView 的滚动何时停止?)

本文介绍了如何准确知道 UIScrollView 的滚动何时停止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简而言之,我需要确切地知道滚动视图何时停止滚动.我所说的停止滚动"是指它不再移动且不再被触摸的那一刻.

In short, I need to know exactly when the scrollview stopped scrolling. By 'stopped scrolling', I mean the moment at which it is no longer moving and not being touched.

我一直在研究一个带有选择选项卡的水平 UIScrollView 子类(适用于 iOS 4).它的要求之一是它在低于一定速度时停止滚动,以允许用户更快地交互.它还应该捕捉到选项卡的开头.换句话说,当用户释放滚动视图并且它的速度很低时,它会捕捉到一个位置.我已经实现了这个并且它可以工作,但是它有一个错误.

I've been working on a horizontal UIScrollView subclass (for iOS 4) with selection tabs in it. One of its requirements is that it stops scrolling below a certain speed to allow user interaction more quickly. It should also snap to the start of a tab. In other words, when the user releases the scrollview and its speed is low, it snaps to a position. I've implemented this and it works, but there's a bug in it.

我现在拥有的:

滚动视图是它自己的委托.在每次调用 scrollViewDidScroll: 时,它都会刷新与速度相关的变量:

The scrollview is its own delegate. at every call to scrollViewDidScroll:, it refreshes its speed-related variables:

-(void)refreshCurrentSpeed
{
    float currentOffset = self.contentOffset.x;
    NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];

    deltaOffset = (currentOffset - prevOffset);
    deltaTime = (currentTime - prevTime);    
    currentSpeed = deltaOffset/deltaTime;
    prevOffset = currentOffset;
    prevTime = currentTime;

    NSLog(@"deltaOffset is now %f, deltaTime is now %f and speed is %f",deltaOffset,deltaTime,currentSpeed);
}

然后根据需要继续捕捉:

Then proceeds to snap if needed:

-(void)snapIfNeeded
{
    if(canStopScrolling && currentSpeed <70.0f && currentSpeed>-70.0f)
    {
        NSLog(@"Stopping with a speed of %f points per second", currentSpeed);
        [self stopMoving];

        float scrollDistancePastTabStart = fmodf(self.contentOffset.x, (self.frame.size.width/3));
        float scrollSnapX = self.contentOffset.x - scrollDistancePastTabStart;
        if(scrollDistancePastTabStart > self.frame.size.width/6)
        {
            scrollSnapX += self.frame.size.width/3;
        }
        float maxSnapX = self.contentSize.width-self.frame.size.width;
        if(scrollSnapX>maxSnapX)
        {
            scrollSnapX = maxSnapX;
        }
        [UIView animateWithDuration:0.3
                         animations:^{self.contentOffset=CGPointMake(scrollSnapX, self.contentOffset.y);}
                         completion:^(BOOL finished){[self stopMoving];}
        ];
    }
    else
    {
        NSLog(@"Did not stop with a speed of %f points per second", currentSpeed);
    }
}

-(void)stopMoving
{
    if(self.dragging)
    {
        [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y) animated:NO];
    }
    canStopScrolling = NO;
}

这里是委托方法:

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    canStopScrolling = NO;
    [self refreshCurrentSpeed];
}

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    canStopScrolling = YES;
    NSLog(@"Did end dragging");
    [self snapIfNeeded];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    [self refreshCurrentSpeed];
    [self snapIfNeeded];
}

这在大多数情况下都很有效,除了以下两种情况:1.当用户在不松开手指的情况下滚动并在移动后立即在接近静止的时间松开时,它通常会按预期的位置捕捉​​到它应该的位置,但很多时候不会.通常需要几次尝试才能实现.时间(非常低)和/或距离(相当高)的奇数值出现在发布时,导致高速值,而实际上滚动视图几乎或完全静止.2. 当用户点击滚动视图停止其移动时,滚动视图似乎将 contentOffset 设置为之前的位置.这种瞬移导致非常高的速度值.这可以通过检查之前的增量是否为 currentDelta*-1 来解决,但我更喜欢更稳定的解决方案.

This works well most of the time, except in two scenarios: 1. When the user scrolls without releasing his/her finger and lets go at a near stationary timing right after moving, it often snaps to its position as it's supposed to, but a lot of times, does not. It usually takes a few attempts to get it to happen. Odd values for time (very low) and/or distance (rather high) appear at the release, causing a high speed value while the scrollView is, in reality, nearly or entirely stationary. 2. When the user taps the scrollview to stop its movement, it seems the scrollview sets the contentOffset to its previous spot. This teleportation results in a very high speed value. This could be fixed by checking if the previous delta is currentDelta*-1, but I'd prefer a more stable solution.

我尝试过使用 didEndDecelerating,但是当故障发生时,它不会被调用.这可能证实它已经是静止的.当滚动视图完全停止移动时,似乎没有调用委托方法.

I've tried using didEndDecelerating, but when the glitch occurs, it does not get called. This probably confirms that it's stationary already. There seems to be no delegate method that gets called when the scrollview stopped moving completely.

如果您想亲自查看故障,这里有一些代码可以用标签填充滚动视图:

If you'd like to see the glitch yourself, here's some code to fill the scrollview with tabs:

@interface  UIScrollView <UIScrollViewDelegate>
{
    bool canStopScrolling;
    float prevOffset;
    float deltaOffset; //remembered for debug purposes
    NSTimeInterval prevTime;
    NSTimeInterval deltaTime; //remembered for debug purposes
    float currentSpeed;
}

-(void)stopMoving;
-(void)snapIfNeeded;
-(void)refreshCurrentSpeed;

@end


@implementation TabScrollView

-(id) init
{
    self = [super init];
    if(self)
    {
        self.delegate = self;
        self.frame = CGRectMake(0.0f,0.0f,320.0f,40.0f);
        self.backgroundColor = [UIColor grayColor];
        float tabWidth = self.frame.size.width/3;
        self.contentSize = CGSizeMake(100*tabWidth, 40.0f);
        for(int i=0; i<100;i++)
        {
            UIView *view = [[UIView alloc] init];
            view.frame = CGRectMake(i*tabWidth,0.0f,tabWidth,40.0f);
            view.backgroundColor = [UIColor colorWithWhite:(float)(i%2) alpha:1.0f];
            [self addSubview:view];
        }
    }
    return self;
}

@end

这个问题的简短版本:如何知道滚动视图何时停止滚动?didEndDecelerating: 当你静止释放它时不会被调用,didEndDragging: 在滚动过程中发生了很多,并且检查速度是不可靠的,因为这种奇怪的跳跃"会设置速度到随机的东西.

A shorter version of this question: how to know when the scrollview stopped scrolling? didEndDecelerating: does not get called when you release it stationary, didEndDragging: happens a lot during the scrolling and checking for speed is unreliable due to this odd 'jump' which sets the speed to something random.

推荐答案

我找到了解决方案:

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

我之前没有注意到最后一点,willDecelerate.结束触摸时滚动视图静止时为假.结合上面提到的速度检查,我可以在它很慢(并且没有被触摸)或静止时捕捉它.

I did not notice that last bit before, willDecelerate. It is false when the scrollView is stationary when ending the touch. Combined with the above-mentioned speed check, I can snap both when it's slow (and not being touched) or when it's stationary.

对于没有进行任何捕捉但需要知道何时停止滚动的任何人,didEndDecelerating 将在滚动运动结束时调用.结合对 didEndDragging 中的 willDecelerate 的检查,您将知道滚动何时停止.

For anyone not doing any snapping but needs to know when scrolling stopped, didEndDecelerating will be called at the end of the scroll movement. Combined with a check on willDecelerate in didEndDragging, you'll know when the scrolling has stopped.

这篇关于如何准确知道 UIScrollView 的滚动何时停止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:如何准确知道 UIScrollView 的滚动何时停止?

基础教程推荐