【发布时间】:2011-12-02 07:04:03
【问题描述】:
简而言之,我需要确切地知道滚动视图何时停止滚动。我所说的“停止滚动”是指它不再移动且不再被触摸的那一刻。
我一直在研究一个带有选择选项卡的水平 UIScrollView 子类(适用于 iOS 4)。它的要求之一是它在低于一定速度时停止滚动,以允许用户更快地交互。它还应该捕捉到选项卡的开头。换句话说,当用户释放滚动视图并且它的速度很低时,它会捕捉到一个位置。我已经实现了这个并且它可以工作,但是它有一个错误。
我现在拥有的:
滚动视图是它自己的委托。在每次调用 scrollViewDidScroll: 时,它都会刷新与速度相关的变量:
-(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);
}
然后根据需要继续捕捉:
-(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 来解决,但我更喜欢更稳定的解决方案。
我尝试过使用didEndDecelerating,但是当故障发生时,它不会被调用。这可能证实它已经是静止的。当滚动视图完全停止移动时,似乎没有调用委托方法。
如果您想亲自查看故障,这里有一些代码可以用标签填充滚动视图:
@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: 在滚动过程中会发生很多事情,并且由于这种奇怪的“跳跃”将速度设置为随机值,因此检查速度是不可靠的。
【问题讨论】:
-
您能否将
didEndDragging与触摸事件结合起来,以便在用户触摸视图时设置一个标志并在他们移开手指时重置。然后,只要设置了标志,您就不会在didEndDragging:下运行相关逻辑。 -
感谢您的回复。但是,我不确定您提到的标志应该是什么。在我看来,您的意思是当滚动视图被释放一次时,标志会显示,但考虑到用户在滚动时多次释放滚动视图,或者仅在静止点而没有抬起他/她的手指。
标签: objective-c ios uiscrollview