UIScrollView - 减速时捕捉控制

Nip*_*rus 7 cocoa-touch uiscrollview ios

UIScrollView中有很多提供给程序员的信息,但我没有看到一个明显的方法来控制位置,控制停止从滚动手势减速后.

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

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

idS*_*tar 27

由于UITableView是UIScrollView子类,因此您可以实现UIScrollViewDelegate方法:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
    withVelocity:(CGPoint)velocity 
    targetContentOffset:(inout CGPoint *)targetContentOffset
Run Code Online (Sandbox Code Playgroud)

然后计算所需的最接近的目标内容偏移量,并在inout CGPoint参数中设置该偏移量.

我刚试过这个,效果很好.

首先,检索非制导偏移,如下所示:

CGFloat unguidedOffsetY = targetContentOffset->y;
Run Code Online (Sandbox Code Playgroud)

然后通过一些数学计算,你想要它,注意表头的高度.以下是我的代码中处理代表美国各州的自定义单元格的示例:

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;
Run Code Online (Sandbox Code Playgroud)

上面的最后一行实际上将值写回到inout参数中,该参数告诉滚动视图这是你想要捕捉的y偏移量.

最后,如果你正在处理一个获取的结果控制器,并且你想知道刚捕到的是什么,你可以做这样的事情(在我的例子中,属性"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;
Run Code Online (Sandbox Code Playgroud)

OFF-TOPIC注意:正如您可能猜到的那样,"log4Debug"语句只是记录.顺便说一句,我正在使用Lumberjack,但我更喜欢旧Log4Cocoa的命令语法.


Fel*_*ino 7

scrollViewDidEndDecelerating:和之后scrollViewDidEndDragging:willDecelerate:(最后一个当减速参数为NO)时,你应该将你的contentOffset参数设置UIScrollView到所需的位置.

您还可以通过检查contentOffset滚动视图的属性来了解当前位置,然后计算最接近的所需区域

虽然您不必创建自己的滚动控件,但您必须手动滚动到所需的位置


小智 7

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

一旦滚动视图开始减速(即在调用scrollViewDidEndDragging:willDecelerate:之后),响应scrollViewDidScroll:并使用前一个scroll事件计算diff,我完成了这个.

当diff小于2到5个像素时,我检查最近的单元格,然后等待该单元格已经过几个像素,然后使用setContentOffset向后滚动另一个方向:animated:.这会产生一点反弹效果,非常适合用户体验,因为它可以对捕捉提供良好的反馈.

当桌子在顶部或底部弹跳时,你必须聪明而不做任何事情(将偏移量与0比较或者内容大小会告诉你).在我的情况下,它工作得很好,因为单元格很小(大约80-100px高),如果你有一个具有更大内容区域的常规滚动视图,你可能会遇到问题.当然,你不会总是减速经过一个单元格,所以在这种情况下我只是滚动到最近的单元格,动画看起来很生涩.事实证明,正确的调整,它几乎没有发生过,所以我对此很酷.花几个小时调整实际值取决于您的特定屏幕,你可以得到一些体面的东西.

我也尝试了天真的方法,调用setContentOffset:animated:on scrollViewDidEndDecelerating:但是它创建了一个非常奇怪的动画(或者如果你没有动画,只是简单的混乱跳跃),越慢,减速率就越低(你'我会从慢动作跳到更快的动作.

所以回答这个问题: - 不,没有简单的方法可以做到这一点,它需要一些时间来抛光以前算法的实际值,这可能在你的屏幕上根本不起作用, - 不要试图创建你自己的滚动视图,你只是浪费时间,并严重重新发明一堆美丽的工程苹果与卡车装载的bug.scrollview委托是您的问题的关键.


Ste*_*n J 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];
}
Run Code Online (Sandbox Code Playgroud)

这利用了int的十进制后数字的下降.还假设您的所有视图都从0,0开始排列,只有contentOffset才能显示在不同的区域.

注意:挂起代理,这非常正常.你得到一个修改版本 - 我只有实际的常量大声笑.我重命名了变量,因此您可以轻松阅读