Sticky scrollview subview has incorrect frame after scrolling fast. Compilable example

Viewed 1279

I have spent the past 12 hours futzing with this and I'm going braindead.

view on bitbucket: https://bitbucket.org/burneraccount/scrollviewexample

git https: https://bitbucket.org/burneraccount/scrollviewexample.git

git ssh: git@bitbucket.org:burneraccount/scrollviewexample.git

^ That is a condensed, self-contained Xcode project that exemplifies the problem I'm having in my real work.

Summary

I am trying to achieve a static-image effect, where the image (a rock in the example) appears stuck to the screen while the lower content scrolls upwards appearing to go above the scrollview.

There's a screen-sized (UIScrollView*)mainScrollView. This scrollview has a (UIView*)contentView. contentView is ~1200 pts long and has two subviews: (UIScrollView*)imageScroller and (UITextView*)textView.

one

All works well until you scroll up a little bit, then scroll down really fast. The resulting position after movement stops is incorrect. It somehow doesn't update properly in the scrollviewDidScroll delegate method. The fast downward scroll (only after you've dragged up) behaves somewhat correctly, but still results in a misplaced view:

two

gently dragging does almost nothing, and the velocity of the scroll is directly related to the incorrect offset.

Here is the offending code:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.mainScrollView)
    {

        CGFloat mainOffset = self.mainScrollView.contentOffset.y;
        if (mainOffset > 0 && mainOffset < self.initialImageScrollerFrame.size.height)
        {
            CGRect imageFrame = self.imageScroller.frame;
            CGFloat offset = mainOffset-self.lastOffset;
            self.imageScroller.frame = CGRectMake(imageFrame.origin.x,
                                                  imageFrame.origin.y + offset, // scroll up
                                                  imageFrame.size.width,
                                                  imageFrame.size.height - offset); // hide the bottom of the image
            self.lastOffset = mainOffset;
        }

    }
}

I've restructured this in almost every way I could imagine and it always has similar effects. The worst part is how the very simple and straightforward code fails to do what is seems like it's guaranteed to do. Also odd is that the zoom-effect I use in my real project works fine using the same mechanism to size the same view element. It runs inside (contentOffset < 0) instead of (contentOffset > 0), so it only zoom in on the imageScroller when you're pulling the view below it's normal offset. That leads me to conspire that some data is lost as it crosses the contentOffset 0, but my conspiracies have been shot down all day.

This is example is a lot less complicated than the real project I'm working on, but it properly reproduces the problem. Please let me know if you can't open my project, or can't connect to the repo.

There is likely a largely obvious fix for this, but I am hours past the point of having any new perspective. I will be thoroughly amazed if I ever get this to work.

4 Answers
Related