How can I programmatically force-stop scrolling in a UIScrollView?

Viewed 107922

Note: The answer given here doesn't work for me.

I have a UIScrollView (not a table view, just a custom thing), and when the user takes certain actions, I want to kill any scrolling (dragging or deceleration) inside the view. I've tried doing e.g. this:

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

on the theory that, given a rect that's already known visible, the scrolling will just stop where it is, but it turns out that this doesn't have any effect-- apparently the scroll view sees that the given rect is in bounds and takes no action. I can get the scroll to stop, if I give a rect that is definitely outside the currently-visible bounds, but inside the contentSize of the view. This seems to halt the view as expected... but also causes it to jump to some other location. I could probably do a little playing around at the margins to get this to work reasonably OK, but does anyone know of a clean way to halt a scroll view that's doing its thing?

Thanks.

14 Answers

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

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.

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)
    }
Related