Mick Byrne
Mick Byrne

Reputation: 14484

UIScrollView - change content offset when dragging ends

I have a scroll view that is programmed so that if you pull down more than 100px (i.e into the negative y content offset and with bounces enabled) then let go, a view at the top of the scroll view gets larger 100px larger (pushing everything else down). To make the transition smooth I'm trying to adjust the content offset of the scrollview by 100px at this point, like this:

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
{
    if scrollView.contentOffset < -100
    {
        scrollView.contentOffset.y += 100 // 
        makeTopViewTaller()
    }
}

However, the change in the contentOffset doesn't stick. By logging the content offset in the scrollViewDidScroll() method I can see that the y value changes for a moment, but then goes back to where it was when the dragging ended.

Is there any way to force the content offset to change, then let the UIScrollView's natural bounce decelaration to apply to the new value?

Upvotes: 1

Views: 1127

Answers (1)

Mick Byrne
Mick Byrne

Reputation: 14484

I tried a whole bunch of different approaches to this problem, but the one that eventually worked involved creating a subclass of the UIScrollView (in my case, actually a UICollectionView) and overriding the setter for the contentOffset property. Here's my subclass:

class MyCollectionView : UICollectionView
{
    var isTransitioning:Bool = false
    var initialTransitioningY:CGFloat = 0

    override var contentOffset:CGPoint
    {
        get
        {
            return super.contentOffset
        }
        // This custom setter is to make the transition to the large header state smooth
        set
        {
            if(isTransitioning && initialTransitioningY < 0)
            {
                var oy = newValue.y
                if(oy == 0 && contentOffset.y < -10) // This is to avoid a flicker when you first call 'reloadData' on the collection view, which sets the contentOffset.y to 0, then returns to it's previous state
                {
                    oy = contentOffset.y
                }
                super.contentOffset = CGPointMake(newValue.x, (initialTransitioningY + 100) * (oy / initialTransitioningY))
            }
            else
            {
                super.contentOffset = newValue
            }
        }
    }
}

When the user has pulled passed the transition point (100px in my case) and let go the scrollViewWillEndDragging method is called and the isTransitioning property is set to true and initialTransitioningY set to the collection view's current content offset. When scrollViewDidEndDecelerating is called the isTransitioning property is set back to false.

Works a charm.

Upvotes: 1

Related Questions