Search code examples
uitableviewcocoa-touchuiscrollviewuiscrollviewdelegate

Updating targetContentOffset in scrollViewWillEndDragging to a value in wrong direction does not animate


I am setting a new value to targetContentOffset in scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) to create a custom paging solution in a UITableView. It works as expected when setting a coordinate for targetContentOffset that is in the same scroll direction as the velocity is pointing.

However, when snapping "backward" in the opposite direction of the velocity it does immediatelly "snap back" without animation. This looks quite bad. Any thoughts on how to solve this.

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    // a lot of caluculations to detemine where to "snap scroll" to
    if shouldSnapScrollUpFromSpeed || shouldSnapScrollUpFromDistance {
        targetContentOffset.pointee.y = -scrollView.frame.height
    } else if shouldSnapScrollDownFromSpeed || shouldSnapScrollDownFromDistance {
        targetContentOffset.pointee.y = -detailViewHeaderHeight
    }
}

I could potentionally calculate when this "bug" will appear and perhaps use another way of "snap scrolling". Any suggestions on how to do this or solve it using targetContentOffset.pointee.y as normal?


Solution

  • I found an okey solution (or workaround). Let me know if anyone have a better solution.

    I just detected when the undesired "non-animated snap scroll" would appear and then instead of setting a new value to targetContentOffset.pointee.y I set it to the current offset value (stopped it) and set the desired offset target value with scrollViews setContentOffset(_:animated:) instead

    if willSnapScrollBackWithoutAnimation {
        targetContentOffset.pointee.y = -scrollView.frame.height+yOffset //Stop the scrolling
        shouldSetTargetYOffsetDirectly = false
    }
    
    if let newTargetYOffset = newTargetYOffset {
        if shouldSetTargetYOffsetDirectly {
            targetContentOffset.pointee.y = newTargetYOffset
        } else {
            var newContentOffset = scrollView.contentOffset
            newContentOffset.y = newTargetYOffset
            scrollView.setContentOffset(newContentOffset, animated: true)
        }
    }