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

注意: 这里给出的答案对我不起作用。

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

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

基于这样一个理论,给定一个已知可见的直角坐标,滚动就会停在它原来的位置,但结果是,这没有任何效果——显然,滚动视图看到给定的直角坐标是在界限内,没有采取任何行动。我 可以得到滚动停止,如果我给一个 rect,肯定是 在外面的当前可见的边界,但在内容大小的视图。这似乎停止了预期的视图... ... 但也导致它跳转到其他地方。我也许可以在页边空白处稍微修改一下,让它工作得合理一些,但是有人知道一种干净利落的方法来停止正在运行的滚动视图吗?

谢谢。

109293 次浏览

The cleanest way will be subclassing UIScrollView and providing your own setContentOffset method. This should pass the message on, only if you haven't switched on your freeze boolean property.

Like so:

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


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

Then, to freeze:

scrollView.freeze = YES;

I played with your original solution a bit, and this seems to work just fine. I think you almost had it, but you were just offsetting the rect that you used too much, and forgot that you could just scroll the rect straight back to the original rect.

The generalized solution for any scrolling action is this:

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

[Edit] As of iOS 4.3 (and possibly earlier) this also appears to work

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

For me, David Lui's accepted answer above didn't work for me. This is what I ended up doing:

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

For what it is worth, I'm using the iOS 6.0 iPhone Simulator.

The generic answer is, that [scrollView setContentOffset:offset animated:NO] is not the same as [scrollView setContentOffset:offset] !

  • [scrollView setContentOffset:offset animated:NO] actually stops any running animation.
  • [scrollView setContentOffset:offset] doesn't stop any running animation.
  • Same for scrollView.contentOffset = offset: doesn't stop any running animation.

That's not documented anywhere, but that's the behavior as tested on iOS 6.1 & iOS 7.1 - probably also before.

So the solution to stop a running animation / deceleration is simple as that:

// Objective-C
[scrollView setContentOffset:scrollView.contentOffset animated:NO];
// Swift
scrollView.setContentOffset(scrollView.contentOffset, animated:false)

Basically what David Liu said in his edited answer. But I wanted to make clear, that these two APIs are NOT the same.

Actually ... The most "modern" way would be -->

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

This deactivates the gesture-recognizer that is responsible for scrolling for just a moment which will kill the current touch. The user would need to lift the finger and put it back down to start scrolling again.

Edit: This actually just kills the current dragging of the user but does not immediately stop the deceleration if the scrollview is in this state currently. To do this the accepted answers edit is pretty much the best way xD

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

This is what I do for my scroll views and all other related subclasses:

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

This sets the targetContentOffset to the scrollView's current offset, thus making the scrolling to stop because it has reached the target. It actually makes sense to use a method whose purpose is that users could set the targeted contentOffset.

Swift

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

This answer worked for me: Deactivate UIScrollView decelerating

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

Stop the scroll in swift:

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

I have tried this methods in collectionview:

self.collectionView.collectionViewLayout.finalizeCollectionViewUpdates()

This works for me in Swift 4.2:

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

... as an extension:

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


}
}

I wanted to disable scrolling only when a certain UIView within the scrollview is the source of the touch during the swipe. It would have required quite a bit of refactoring to move the UIView outside of the UIScrollView, as we had a complex view hierarchy.

As a workaround, I added a single UIPanGestureRecognizer to the subview in which I wanted to prevent from scrolling. This UIPanGestureRecognizer will cancelsTouchesInView which prevents the UIScrollView's panGesture from activating.

It's a little bit of a 'hack', but it's a super easy change, and if you're using a XIB or Storyboard, all you need to do is drag the pan gesture onto the subview in question.

Disable just scroll user interaction. (swift)

scrollView.isScrollEnabled = false

Disable in during scroll animation after dragging. (swift)

var scrollingByVelocity = false


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

SWift 5+

by using Extension

extension UIScrollView  {
    

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

use

    myScrollView.stopDecelerating()
// your stuff

Swift 5, Xcode 13

In my example, I have the situation where the user has a scroll view and image inside, and when the user begins zooming the image need prevent free-scrolling inside, I use the delegate function of the scroll view and content offset. Don't forget scrollView.delegate = self, if need full code don't shy notify my My code:

private var contentOffsetHolder: CGPoint = CGPoint(x: 0, y: 0)
    

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

}
    

func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
scrollView.setContentOffset(contentOffsetHolder, animated:false)
}