2011-05-17 73 views
7

UIScrollView有很多信息可供程序员使用,但我没有看到明显的方式来控制从滚动手势减速后控件停止的位置。UIScrollView - 捕捉减速时的控制

基本上我想滚动视图捕捉到屏幕的特定区域。用户仍然可以像正常一样滚动,但是当它们停止滚动时,视图应该捕捉到最相关的位置,并且在轻弹手势的情况下,减速也应该在这些位置处停止。

有没有简单的方法来做这样的事情,或者我应该考虑完成这种效果来编写自定义滚动控件的唯一方法?

回答

7

scrollViewDidEndDecelerating:scrollViewDidEndDragging:willDecelerate:(最后一个正当的减速参数是NO)后,你应该设置你的UIScrollView到所需位置的contentOffset参数。

你也知道通过检查你的滚动型的contentOffset属性的当前位置,然后计算出你有

虽然你不必创建自己的滚动控制最接近期望的区域,你将有手动滚动到所需的位置

7

要添加到felipe说,我最近创建了一个表格视图,以UIPicker类似的方式捕捉单元格。 一个聪明的scrollview委托绝对是这样做的(你也可以在uitableview上做到这一点,因为它只是uiscrollview的子类)。

我这个做的,一旦滚动视图开始减速(即scrollViewDidEndDragging后:willDecelerate:叫),响应scrollViewDidScroll:和计算与先前的滚动事件的差异。

当diff小于2至5个像素时,我检查最近的单元格,然后等待该单元格已经过了几个像素,然后用setContentOffset向另一个方向滚动:animated: 。 这会创建一个对用户体验非常好的小反弹效果,因为它可以为拍摄提供良好的反馈。

当表格在顶部或底部弹跳时(将偏移量与0进行比较或内容大小会告诉您这一点),您必须非常聪明,不要做任何事情。 在我的情况下它工作得很好,因为单元格很小(大约80-100px),如果您有一个具有较大内容区域的常规滚动视图,则可能会遇到问题。 当然,你不会总是减速通过一个单元格,所以在这种情况下,我只是滚动到最近的单元格,动画看起来很生涩。发现正确的调整,它几乎没有发生,所以我很酷。 花几个小时根据具体的屏幕调整实际值,你可以得到一些体面的。

我也试过天真的态度,要求setContentOffset:动画:在scrollViewDidEndDecelerating:但它创建了一个很奇怪的动画(或者只是普通的混乱跳,如果你不动画),变得更糟减速率越低是(你会从缓慢的运动跳到更快的运动)。

因此,要回答这个问题: - 不,有没有简单的方法来做到这一点,它会需要一些时间打磨以前的算法,这可能不是你的屏幕, 不惜一切工作的实际值 - 不要试图创建自己的滚动视图,你只需要浪费时间并重新创建一个由卡车装载的bug创建的漂亮工程苹果。 scrollview委托是你的问题的关键。

+0

谢谢@kra,这绝对会创建一个更流畅的动画,并且scrollview感觉更自然! – 2014-02-15 21:07:08

27

由于UITableView的是一个UIScrollView子类,你可以实现的UIScrollViewDelegate方法:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
    withVelocity:(CGPoint)velocity 
    targetContentOffset:(inout CGPoint *)targetContentOffset 

,然后计算一下最接近期望的目标内容偏移是你想要的,并设置在INOUT CGPoint参数。

我刚刚试过这个,它运作良好。

首先,检索制导这样的偏移:

CGFloat unguidedOffsetY = targetContentOffset->y; 

然后通过一些数学,在那里你会希望它是弄清楚,注意表头的高度。下面是我的代码处理代表美国各州自细胞样本:

CGFloat guidedOffsetY; 

if (unguidedOffsetY > kFirstStateTableViewOffsetHeight) { 
    int remainder = lroundf(unguidedOffsetY) % lroundf(kStateTableCell_Height_Unrotated); 
    log4Debug(@"Remainder: %d", remainder); 
    if (remainder < (kStateTableCell_Height_Unrotated/2)) { 
     guidedOffsetY = unguidedOffsetY - remainder; 
    } 
    else { 
     guidedOffsetY = unguidedOffsetY - remainder + kStateTableCell_Height_Unrotated; 
    } 
} 
else { 
    guidedOffsetY = 0; 
} 

targetContentOffset->y = guidedOffsetY; 

上面的最后一行,实际写入值回INOUT参数,它告诉滚动认为,这是y偏移你” d喜欢它捕捉。最后,如果你正在处理一个抓取的结果控制器,并且你想知道刚刚捕捉到的结果,你可以做这样的事情(在我的例子中,属性“states”是美国国家的FRC )。我使用该信息来设置按钮标题:

NSUInteger selectedStateIndexPosition = floorf((guidedOffsetY + kFirstStateTableViewOffsetHeight)/kStateTableCell_Height_Unrotated); 
log4Debug(@"selectedStateIndexPosition: %d", selectedStateIndexPosition); 
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:selectedStateIndexPosition inSection:0]; 
CCState *selectedState = [self.states objectAtIndexPath:indexPath]; 
log4Debug(@"Selected State: %@", selectedState.name); 
self.stateSelectionButton.titleLabel.text = selectedState.name; 

题外话注:正如你可能猜到了,“log4Debug”语句只是记录。顺便说一下,我为此使用了Lumberjack,但我更喜欢旧Log4Cocoa中的命令语法。

+0

谢谢!超级有用。 – theVurt 2012-07-09 12:30:49

+0

推动可滚动视图控制器时,contentOffset将考虑UINavigationBar高度和原点。 (不知道为什么它这样做,因为这是没有道理的,但它就是这样) – cynistersix 2014-05-30 00:02:45

6

尝试这样:

- (void) snapScroll; 
{ 
    int temp = (theScrollView.contentOffset.x+halfOfASubviewsWidth)/widthOfSubview; 
    theScrollView.contentOffset = CGPointMake(temp*widthOfSubview , 0); 
} 

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

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

这需要的小数点后的位数INT的下降的优势。同样假设你的所有视图都是从0,0排列起来的,只有contentOffset是它在不同区域显示的内容。

注意:挂钩委托,这工作得很好。你得到一个修改版本 - 我的实际常量大声笑。我重命名了变量,以便您可以轻松读取它