Setting contentOffset programmatically triggers scrollViewDidScroll

IosUiscrollviewUiscrollviewdelegate

Ios Problem Overview


I've got a a few UIScrollView on a page. You can scroll them independently or lock them together and scroll them as one. The problem occurs when they are locked.

I use UIScrollViewDelegate and scrollViewDidScroll: to track movement. I query the contentOffset of the UIScrollView which changed and then reflect change to other scroll views by setting their contentOffset property to match.

Great.... except I noticed a lot of extra calls. Programmatically changing the contentOffset of my scroll views triggers the delegate method scrollViewDidScroll: to be called. I've tried using setContentOffset:animated: instead, but I'm still getting the trigger on the delegate.

How can I modify my contentOffsets programmatically to not trigger scrollViewDidScroll:?

Implementation notes.... Each UIScrollView is part of a custom UIView which uses delegate pattern to call back to the presenting UIViewController subclass that handles coordinating the various contentOffset values.

Ios Solutions


Solution 1 - Ios

It is possible to change the content offset of a UIScrollView without triggering the delegate callback scrollViewDidScroll:, by setting the bounds of the UIScrollView with the origin set to the desired content offset.

CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = desiredContentOffset;
scrollView.bounds = scrollBounds;

Solution 2 - Ios

Try

id scrollDelegate = scrollView.delegate;
scrollView.delegate = nil;
scrollView.contentOffset = point;
scrollView.delegate = scrollDelegate;

Worked for me.

Solution 3 - Ios

What about using existing properties of UIScrollView?

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
        /// The content offset was changed programmatically.
        /// Your code goes here.
    }
}

Solution 4 - Ios

Another approach is to add some logic in your scrollViewDidScroll delegate to determine whether or not the change in content offset was triggered programatically or by the user's touch.

  • Add an 'isManualScroll' boolean variable to your class.
  • Set its initial value to false.
  • In scrollViewWillBeginDragging set it to true.
  • In your scrollViewDidScroll check to see that is it true and only respond if it is.
  • In scrollViewDidEndDecelerating set it to false.
  • In scrollViewWillEndDragging add logic to set it to false if the velocity is 0 (as scrollViewDidEndDecelerating won't be called in this case).

Solution 5 - Ios

Simplifying @Tark's answer, you can position the scrollview without firing scrollViewDidScroll in one line like this:

scrollView.bounds.origin = CGPoint(x:0, y:100); // whatever values you'd like

Solution 6 - Ios

This is not a direct answer to the question, but if you are getting what appear to be spurious such messages, it can ALSO be because you are changing the bounds. I am using some Apple sample code with a "tilePages" method that removes and adds subview to a scrollview. This infrequently results in additional scrollViewDidScroll: messages called immediately, so you get into a recursion which you for sure didn't expect. In my case I got a nasty impossible to find crash.

What I ended up doing was queuing the call on the main queue:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
	if(scrollView == yourScrollView) {
		// dispatch fixes some recursive call to scrollViewDidScroll in tilePages (related to removeFromSuperView)
		// The reason can be found here: http://stackoverflow.com/questions/9418311
		dispatch_async(dispatch_get_main_queue(), ^{ [self tilePages]; });
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDBDView Question on Stackoverflow
Solution 1 - IosTarkView Answer on Stackoverflow
Solution 2 - IosAbhijitView Answer on Stackoverflow
Solution 3 - IosKukoskView Answer on Stackoverflow
Solution 4 - IosJake MacMullinView Answer on Stackoverflow
Solution 5 - IosSimplGyView Answer on Stackoverflow
Solution 6 - IosDavid HView Answer on Stackoverflow