如何以编程方式强制停止UIScrollView中的滚动?


80

注意:此处给出答案对我不起作用。

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

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

根据这样的理论,给定一个已知的rect,滚动将停在原处,但事实证明这没有任何作用-显然,滚动视图看到给定的rect处于边界并采取了没有行动。我可以得到滚动停下来,如果我给一个矩形,那绝对是外界目前可见的范围,但视图的contentSize内。这似乎像预期的那样暂停了视图...但是也导致其跳到其他位置。我可能会在空白处稍作尝试,以使其能够正常运行,但是有人知道停止该功能的滚动视图的干净方法吗?

谢谢。

Answers:


108

我使用了您原来的解决方案,这似乎很好用。我认为您几乎拥有了它,但是您只是抵消了使用过多的矩形,而忘记了可以直接将其滚动回原始矩形。

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

- (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];
}

1
大卫:太好了,谢谢。我一直在朝着这个方向扭动自己,但最终得到了一些琐碎的计算,涉及在滚动范围之外选择一个1x1矩形。采纳您建议的偏移量然后立即恢复的建议(坦率地说,这是为了利用未发布的行为,即使“结果”应该是空操作,一个事件中的连续调用实际上仍在运行),它可以正常工作。我将在上面编辑您的答案,以包括适用于任何滚动方向的通用解决方案。谢谢!
本·佐托

(希望您不介意编辑,希望为以后的旅客澄清。感谢您的回答!)
Ben Zotto 2010年

看起来很棒...顺便问一下,如何回到原来的状态?您在消除偏移之前先保存偏移,然后回到那里吗?
Lior Frenkel

2
在iOS 7中,当scrollView为Decelerating时,只有顶部解决方案有效(+ = 1,-= 1解决方案)。
萨希尔2014年

1
此解决方案可以改进。使用+ = 0.1代替+ = 1,因此单次调用setContentOffset就足够了。滚动视图将自动舍入内容偏移量。
凯林2015年

91

通用的答案是,这[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];

基本上是David Liu在编辑后的回答中所说的。但我想清楚,这两个API是一样的。

斯威夫特3:

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

11
对我们中间的“寻求理由”的解释做得很好...(:
Aviel Gross 2015年

3
这有点让我难过。
DCMaxxx

2
我不明白为什么没有记录这些内容... urggggghh
user1105951

在(至少)iOS 11.3和11.4中更有趣的是:如果将contentOffset设置为动画,例如(300,0),则在动画仍运行时分配了“ contentOffset =(300,0)”,contentOffset可能会重置为(0,0)!这一定是一个错误!

68

对我来说,上面David Lui接受的答案对我不起作用。这就是我最终要做的事情:

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

对于它的价值,我正在使用iOS 6.0 iPhone Simulator。


2
就像您说的+1一样,另一个也不适合我,但是这种解决方案效果很好!
brenjt

1
太棒了,supportedInterfaceOrientations如果用户在滚动时旋转设备,它可以完美地防止滚动事件继续进行,这有时会很混乱,具体取决于您在做什么。
cprcrack

21

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

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

这台targetContentOffsetscrollView目前的偏移,从而使滚动停止,因为它已经达到了目标。使用目的是用户可以设置目标的方法实际上是有意义的contentOffset

迅速

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

这看起来像是开发人员应该使用的一个苹果。因为其他所有东西都会在某个时候导致错误。我很惊讶这不是公认的答案。
OlDor

这就是该委托方法的目的。好答案。
瑞安

在我看来,这是Apple在其文档中所说的最优雅的解决方案:“您的应用程序可以更改targetContentOffset参数的值,以调整scrollview完成滚动动画的位置。” 对我来说做得很好
Macistador '17

这是正确的答案。以上都不对我
有用

20

迅速停止滚动:

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

16

实际上...最“现代”的方式是->

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

这将禁用手势识别器,该手势识别器仅滚动一会儿会杀死当前的触摸。用户将需要抬起手指并将其放回原位以再次开始滚动。

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

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

好吧,我在我的一个App中使用了它,它的功能完全符合OP的要求。您能再给我一点背景吗?什么不起作用?为什么?
Xatian

哦,对不起。。。我想我知道了。。。我只是回答了我要查找的部分问题……我忽略了的第二部分……:-)->已编辑。
Xatian


5

最干净的方法是子类化UIScrollView并提供您自己的setContentOffset方法。当您尚未打开freeze布尔属性时,这才应该传递消息。

像这样:

BOOL freeze; // and the @property, @synthesize lines..

-(void)setContentOffset:(CGPoint)offset
{
    if ( !freeze ) [super setContentOffset:offset];
}

然后,冻结:

scrollView.freeze = YES;

1
谢谢。尽管这会停止可见的滚动,但实际上并没有停止内部减速。如果将其打开,然后过一会儿再关闭,您将看到滚动暂停,然后向前跳并继续减速。因此,scrollview的内部状态仍在滚动。因此,您需要冻结直到知道减速停止为止,您可以与代表合作进行减速,但是这对我来说有点愚蠢。我希望实际上能立即降低速度的东西(例如,在屏幕上轻按一下即可)。
Ben Zotto

好的,从以前的经验来看,这类问题听起来很熟悉……为什么不通过设置decelerationRate = 1e10;freeze == YES来设置减速速度?
mvds

(不知道内部的数学,10 * UIScrollViewDecelerationRateFast可以是明智的选择比1E10)
MVDS

有趣的主意!不幸的是,该值似乎受到限制。进行了尝试,似乎没有值会导致减速立即停止。我能做的最好就是让它变慢。:)
Ben Zotto

计划B然后将在短时间内禁用滚动-也许您可以摆脱它,因为无论如何您都将阻止setContentOffset,希望可以防止禁用滚动的副作用。
mvds


3

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

scrollView.isScrollEnabled = false

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

var scrollingByVelocity = false

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

0

我在collectionview中尝试了以下方法:

self.collectionView.collectionViewLayout.finalizeCollectionViewUpdates()


0

这在Swift 4.2中对我有用

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

...作为扩展:

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

    }
}

0

我只想在滚动视图中某个UIView是触摸期间的触摸源时才禁用滚动。由于我们具有复杂的视图层次结构,因此需要大量重构才能将UIView移出UIScrollView。

解决方法是,我在子视图中添加了一个UIPanGestureRecognizer,以防止滚动。此UIPanGestureRecognizer将cancelsTouchesInView阻止UIScrollView的panGesture激活。

这有点“ hack”,但这是一个超级容易的更改,如果您使用的是XIB或Storyboard,则需要做的就是将平移手势拖动到相关的子视图上。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.