Setting contentOffset programmatically triggers scrollViewDidScroll

痞子三分冷 提交于 2019-12-17 15:19:25

问题


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.


回答1:


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;



回答2:


Try

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

Worked for me.




回答3:


What about using existing properties of UIScrollView?

if(scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating) {
    //your code
}



回答4:


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



回答5:


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).



回答6:


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


来源:https://stackoverflow.com/questions/9418311/setting-contentoffset-programmatically-triggers-scrollviewdidscroll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!