【问题标题】:UIScrollView - snap to control when deceleratingUIScrollView - 减速时捕捉控制
【发布时间】:2011-08-27 15:12:07
【问题描述】:

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

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

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

【问题讨论】:

    标签: cocoa-touch ios uiscrollview


    【解决方案1】:

    由于 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 偏移量。

    最后,如果您正在处理获取的结果控制器,并且您想知道刚刚捕捉到的内容,您可以执行以下操作(在我的示例中,属性“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 中的命令语法。

    【讨论】:

    • 当推动可滚动视图控制器时,contentOffset 将考虑 UINavigationBar 的高度和原点。 (不知道为什么这样做,因为这没有任何意义,但这就是它的作用)
    【解决方案2】:

    scrollViewDidEndDecelerating:scrollViewDidEndDragging:willDecelerate: 之后(将减速参数为NO 的最后一个)您应该将UIScrollViewcontentOffset 参数设置为所需的位置。

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

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

    【讨论】:

      【解决方案3】:

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

      一旦滚动视图开始减速(即在调用 scrollViewDidEndDragging:willDecelerate: 之后),我已经完成了这项工作,响应 scrollViewDidScroll: 并计算与前一个滚动事件的差异。

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

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

      我也尝试过幼稚的方法,在 scrollViewDidEndDecelerating: 上调用 setContentOffset:animated: 但它会创建一个非常奇怪的动画(或者如果你不制作动画则只是简单的令人困惑的跳跃),减速率越低越糟糕(你会从一个慢动作跳到一个更快的动作)。

      所以回答这个问题: - 不,没有简单的方法可以做到这一点,这需要一些时间来完善以前算法的实际值,这可能在您的屏幕上根本不起作用, - 不要试图创建你自己的滚动视图,你只会浪费时间并严重地重新发明一个用卡车负载创建的漂亮的工程苹果。滚动视图委托是解决问题的关键。

      【讨论】:

      • 感谢@kra,这绝对可以创建更流畅的动画,并且滚动视图感觉更自然!
      【解决方案4】:

      试试这样的:

      - (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 是使其显示在不同区域的原因。

      注意:连接代理,这工作得很好。你得到了一个修改后的版本——我的只有实际的常量,哈哈。我重命名了变量,以便您可以轻松阅读

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-12
        • 1970-01-01
        • 1970-01-01
        • 2013-08-26
        • 2013-04-29
        • 2010-11-09
        相关资源
        最近更新 更多