Abe*_*ant 26 objective-c uiscrollview ios
简而言之,我需要确切知道scrollview何时停止滚动.通过"停止滚动",我的意思是它不再移动而不被触摸的那一刻.
我一直在研究一个水平的UIScrollView子类(适用于iOS 4),其中包含选择标签.它的一个要求是它停止滚动到一定速度以下,以便更快地进行用户交互.它还应该捕捉到选项卡的开头.换句话说,当用户释放滚动视图并且其速度较低时,它会捕捉到一个位置.我实现了它并且它可以工作,但它有一个错误.
我现在拥有的:
scrollview是它自己的委托.每次调用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);
}
Run Code Online (Sandbox Code Playgroud)
然后根据需要继续捕捉:
-(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;
}
Run Code Online (Sandbox Code Playgroud)
以下是委托方法:
-(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];
}
Run Code Online (Sandbox Code Playgroud)
这在大多数情况下都很有效,除了在两种情况下:1.当用户滚动而不释放他/她的手指并且在移动后立即进入接近静止的时间时,它通常会按照它应该的位置捕捉到它的位置,但是很多时候,没有.通常需要几次尝试才能实现它.时间(非常低)和/或距离(相当高)的奇数值出现在释放时,导致高速值,而scrollView实际上几乎或完全静止.2.当用户点击滚动视图以停止其移动时,似乎滚动视图将contentOffset
其设置为其先前的位置.这种远距传送产生非常高的速度值.这可以通过检查先前的delta是否为currentDelta*-1来解决,但我更喜欢更稳定的解决方案.
我尝试过使用didEndDecelerating
,但是当出现故障时,它不会被调用.这可能证实它已经静止了.似乎没有在scrollview完全停止移动时调用的委托方法.
如果您想亲眼看到这些故障,可以使用以下选项卡填充scrollview:
@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
Run Code Online (Sandbox Code Playgroud)
这个问题的一个较短版本:如何知道滚动视图何时停止滚动?didEndDecelerating:
当你静止释放它时不会被调用,didEndDragging:
在滚动过程中会发生很多事情,并且检查速度是不可靠的,因为这种奇怪的"跳跃" 会将速度设置为随机的.
Abe*_*ant 30
我找到了解决方案:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
Run Code Online (Sandbox Code Playgroud)
我没注意到最后一点,willDecelerate
.当结束触摸时,scrollView静止时为false.结合上面提到的速度检查,我可以在它缓慢(不被触摸)或静止时拍摄.
对于没有进行任何捕捉但需要知道何时滚动停止的人,didEndDecelerating
将在滚动运动结束时调用.与检查相结合willDecelerate
的didEndDragging
,当滚动停止,你就会知道.
Dav*_*d H 21
[编辑答案]这是我使用的 - 它处理所有边缘情况.你需要一个ivar来保持状态,并且如评论中所示,还有其他方法来处理这个问题.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
//[super scrollViewWillBeginDragging:scrollView]; // pull to refresh
self.isScrolling = YES;
NSLog(@"+scrollViewWillBeginDragging");
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
//[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; // pull to refresh
if(!decelerate) {
self.isScrolling = NO;
}
NSLog(@"%@scrollViewDidEndDragging", self.isScrolling ? @"" : @"-");
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidEndDecelerating");
}
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidScrollToTop");
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
self.isScrolling = NO;
NSLog(@"-scrollViewDidEndScrollingAnimation");
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个非常简单的项目使用上面的代码,这样当一个人与scrollView(包括WebView)交互时,它会禁止进程密集型工作,直到用户停止与scrollView交互并且scrollview停止滚动.这就像50行代码:ScrollWatcher
Way*_*ett 19
以下是滚动完成后如何组合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
}
Run Code Online (Sandbox Code Playgroud)