【问题标题】:How can I programmatically force-stop scrolling in a UIScrollView?如何以编程方式强制停止 UIScrollView 中的滚动?
【发布时间】:2011-03-25 13:22:51
【问题描述】:

注意:answer given here 对我不起作用。

我有一个 UIScrollView(不是表格视图,只是一个自定义的东西),当用户执行某些操作时,我想终止视图内的任何滚动(拖动或减速)。我试过做例如这个:

[scrollView scrollRectToVisible:CGRectInset([scrollView bounds], 10, 10) animated:NO];

理论上,给定一个已知可见的矩形,滚动将停止在它所在的位置,但事实证明这没有任何效果 - 显然滚动视图看到给定的矩形在界限并且不采取任何行动。我可以让滚动停止,如果我给出一个绝对在当前可见边界之外的矩形,但在视图的 contentSize 之内。这似乎按预期停止了视图......但也导致它跳转到其他位置。我可能可以在边缘进行一些操作以使其正常工作,但是有没有人知道一种干净的方法来停止正在执行它的滚动视图?

谢谢。

【问题讨论】:

    标签: iphone uiscrollview


    【解决方案1】:

    我对您的原始解决方案进行了一些尝试,这似乎工作得很好。我认为您几乎拥有它,但是您只是抵消了使用过多的矩形,而忘记了可以将矩形直接滚动回原始矩形。

    任何滚动动作的通用解决方案是这样的:

    - (void)killScroll 
    {
        CGPoint offset = scrollView.contentOffset;
        offset.x -= 1.0;
        offset.y -= 1.0;
        [scrollView setContentOffset:offset animated:NO];
        offset.x += 1.0;
        offset.y += 1.0;
        [scrollView setContentOffset:offset animated:NO];
    }
    

    [编辑] 从 iOS 4.3(可能更早)开始,这似乎也可以工作

    - (void)killScroll 
    {
        CGPoint offset = scrollView.contentOffset;
        [scrollView setContentOffset:offset animated:NO];
    }
    

    【讨论】:

    • 大卫:太好了,谢谢。我一直在这个方向上玩弄自己,但最终得到了关于在滚动边界之外选择一个 1x1 矩形的不那么琐碎的计算。采纳您的抵消建议,然后立即恢复(坦率地说,这似乎是为了利用未发布的行为,即在一个事件中的连续调用实际上可以运行,即使“结果”应该是无操作的),它工作正常。我将在上面编辑您的答案,以包含适用于任何滚动方向的通用解决方案。谢谢!
    • (希望您不介意修改,希望为后来的旅行者澄清一下。感谢您的回答!)
    • 看起来很棒...顺便问一下,您如何回到原来的位置?你会在杀死它之前保存偏移量,然后回到那里吗?
    • 在 iOS 7 中,当 scrollView 处于减速状态时,只有顶部的解决方案有效(+= 1,-= 1 的解决方案)。
    • 这个解决方案可以改进。使用 += 0.1 而不是 += 1,所以一次调用 setContentOffset 就足够了。滚动视图会自动对内容偏移量进行四舍五入。
    【解决方案2】:

    一般的答案是,[scrollView setContentOffset:offset animated:NO] 与[scrollView setContentOffset:offset]不一样!

    • [scrollView setContentOffset:offset animated:NO] 实际上会停止任何正在运行的动画。
    • [scrollView setContentOffset:offset] 不会停止任何正在运行的动画。
    • scrollView.contentOffset = offset 也一样:不会停止任何正在运行的动画。

    这在任何地方都没有记录,但这是在 iOS 6.1 和 iOS 7.1 上测试过的行为——可能之前也是如此。

    所以停止正在运行的动画/减速的解决方案很简单:

    [scrollView setContentOffset:scrollView.contentOffset animated:NO];
    

    基本上是大卫刘在他编辑的答案中所说的。但我想澄清一下,这两个 API 不相同。

    斯威夫特 3:

    scrollView.setContentOffset(scrollView.contentOffset, animated:false)
    

    【讨论】:

    • 对我们中间的“理性寻求者”的解释做得很好......(:
    • 这有点让我难过。
    • 我不明白为什么这些东西没有记录在案...urggggghh
    • 在(至少)iOS 11.3 和 11.4 中更有趣的是:如果 contentOffset 动画到例如(300, 0),然后在动画仍在运行时分配“contentOffset = (300, 0)”,contentOffset 可能会重置为 (0, 0)!这一定是个bug!
    【解决方案3】:

    对我来说,David Lui's accepted answer above 不适合我。这就是我最终要做的:

    - (void)killScroll {
        self.scrollView.scrollEnabled = NO;
        self.scrollView.scrollEnabled = YES;
    }
    

    不管怎样,我使用的是 iOS 6.0 iPhone 模拟器。

    【讨论】:

    • +1 就像你说的,另一个也不适合我,但这个解决方案效果很好!
    • 太棒了,它在supportedInterfaceOrientations 中完美运行,以防止在用户在滚动时旋转设备时继续发生滚动事件,这有时会造成混乱,具体取决于您的内容正在做。
    【解决方案4】:

    这是我为滚动视图和所有其他相关子类所做的:

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
     {
        *targetContentOffset = scrollView.contentOffset;
     }
    

    这会将targetContentOffset 设置为scrollView 的当前偏移量,从而使滚动停止,因为它已到达目标。 使用目的是用户可以设置目标contentOffset的方法实际上是有意义的。

    斯威夫特

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        targetContentOffset.pointee = scrollView.contentOffset
    }
    

    【讨论】:

    • 这看起来像是苹果应该使用的开发人员。因为所有其他人在某些时候都会导致错误。我很惊讶这不是公认的答案。
    • 这就是这个委托方法的用途。很好的答案。
    • 在我看来,这是最优雅的解决方案,正如 Apple 在其文档中所说:“您的应用程序可以更改 targetContentOffset 参数的值来调整滚动视图完成其滚动动画的位置。”。对我来说效果很好
    • 这是正确的答案。以上都不适合我
    【解决方案5】:

    在 swift 中停止滚动:

    scrollView.setContentOffset(scrollView.contentOffset, animated: false)
    

    【讨论】:

      【解决方案6】:

      其实...最“现代”的方式应该是 -->

      scrollview.panGestureRecognizer.enabled = false;
      scrollview.panGestureRecognizer.enabled = true;
      

      这会停用负责滚动片刻的手势识别器,这将终止当前的触摸。用户需要抬起手指然后放回去才能再次开始滚动。

      编辑:这实际上只是杀死了用户当前的拖动,但如果滚动视图当前处于此状态,则不会立即停止减速。为此,接受的答案编辑几乎是最好的方法 xD

      [scrollview setContentOffset: scrollview.contentOffset animated:false];
      

      【讨论】:

      • 好吧,我在我的一个应用程序中使用它,它完全按照 OP 的要求执行。您能否提供更多背景信息?什么不工作或为什么?
      • 哦,抱歉……我想我知道……我刚刚回答了我正在寻找的问题的一部分……我忽略了第二部分……:-) -->已编辑。
      • 不停止减速。
      【解决方案7】:

      最简洁的方法是继承 UIScrollView 并提供您自己的 setContentOffset 方法。这应该会传递消息,仅如果您还没有打开您的 freeze 布尔属性。

      像这样:

      BOOL freeze; // and the @property, @synthesize lines..
      
      -(void)setContentOffset:(CGPoint)offset
      {
          if ( !freeze ) [super setContentOffset:offset];
      }
      

      然后,冻结:

      scrollView.freeze = YES;
      

      【讨论】:

      • 谢谢。虽然这会停止可见的滚动,但实际上并没有停止内部减速。如果您将其打开,然后稍后再关闭,您将看到滚动暂停,然后向前跳并继续减速。所以scrollview的内部状态还在滚动。因此,您需要冻结,直到您知道减速已经停止,您可以与代表合作执行此操作,但对我来说感觉有点傻。我希望有一些实际的东西可以立即消除减速(例如,在屏幕上单击一下效果)。
      • 好的,从以前的经验来看,这些问题听起来很熟悉......为什么不通过设置 decelerationRate = 1e10; 而 freeze == YES 来消除减速?
      • (不知道内部数学,10*UIScrollViewDecelerationRateFast 可能是比 1e10 更明智的选择)
      • 有趣的想法!不幸的是,这个值似乎被限制了。试过了,似乎没有这个值会导致减速立即停止。我能做的最好的就是让它变得迟钝。 :)
      • 计划 B 然后是在短时间内禁用滚动 - 也许你可以侥幸逃脱,因为无论如何你都在阻止 setContentOffset,希望能防止禁用滚动的副作用。
      【解决方案8】:

      这个答案对我有用: Deactivate UIScrollView decelerating

      -(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
          [scrollView setContentOffset:scrollView.contentOffset animated:YES];
      }
      

      【讨论】:

      • 这可能会在反弹期间停止滚动视图
      【解决方案9】:

      禁用仅滚动用户交互。 (迅速)

      scrollView.isScrollEnabled = false
      

      在拖动后的滚动动画期间禁用。 (迅速)

      var scrollingByVelocity = false
      
      func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
          if !scrollingByVelocity {
              scrollView.setContentOffset(scrollView.contentOffset, animated: false)
          }
      }
      

      【讨论】:

        【解决方案10】:

        这适用于 Swift 4.2:

           func killScroll() {
            self.scrollView.isScrollEnabled = false;
            self.scrollView.isScrollEnabled = true;
        }
        

        ...作为扩展:

        extension UIScrollView {
            func killScroll() {
                self.isScrollEnabled = false;
                self.isScrollEnabled = true;
        
            }
        }
        

        【讨论】:

          【解决方案11】:

          斯威夫特 5+

          通过使用扩展

          extension UIScrollView  {
              
              func stopDecelerating() {
                  let contentOffset = self.contentOffset
                  self.setContentOffset(contentOffset, animated: false)
              }
          }
          

          使用

              myScrollView.stopDecelerating()
              // your stuff
          

          【讨论】:

            【解决方案12】:

            我在collectionview中试过这个方法:

            self.collectionView.collectionViewLayout.finalizeCollectionViewUpdates()

            【讨论】:

              【解决方案13】:

              我只想在滚动视图中的某个 UIView 是滑动期间的触摸源时禁用滚动。将 UIView 移到 UIScrollView 之外需要进行相当多的重构,因为我们有一个复杂的视图层次结构。

              作为一种解决方法,我将一个 UIPanGestureRecognizer 添加到我想要阻止滚动的子视图中。此 UIPanGestureRecognizer 将 cancelsTouchesInView 阻止 UIScrollView 的 panGesture 激活。

              这有点像“hack”,但它是一个超级简单的更改,如果您使用的是 XIB 或 Storyboard,您只需将平移手势拖到相关的子视图上即可。

              【讨论】:

                猜你喜欢
                • 2015-08-28
                • 2011-12-13
                • 2011-01-15
                • 2010-11-02
                • 1970-01-01
                • 1970-01-01
                • 2023-04-06
                相关资源
                最近更新 更多