Screen Shot 2020-05-06 at 17 37 05

When the dragging of Outer UIScrollView is ended, and it continues decelerating, we want to transfer that decelerating to the Nested UIScrollView to make users feel they’re only one UIScrollView

Decelerator

I was inspired by this nice article about Deceleration mechanics of UIScrollView

So this article will show you how to apply it to Outer/Nested UIScrollView 👆

I create a class called ScrollingDecelerator, and it will manage the transfering decelerating between outer and nested UIScrollView.

How to use

Downward Decelerating: Outer UIScrollView ==> Nested UIScrollView

For the Outer UIScrollView:

    var outerDeceleration: ScrollingDeceleration?

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        outerDeceleration = ScrollingDeceleration(velocity: velocity, decelerationRate: scrollView.decelerationRate)
    }

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        outerDeceleration = nil
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        outerDeceleration = nil
    }

In some place where you need to pass the outerDeceleration into the Nested UIScrollView

Continue downward decelerating for the Nested UIScrollView:

    let nestedScrollingDecelerator = ScrollingDecelerator(scrollView: nestedScrollView)

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        nestedScrollingDecelerator.invalidateIfNeeded()
    }

When the Nested UIScrollView got the outerDeceleration, we can continue the downward decelerating by:

    nestedScrollingDecelerator.decelerate(by: outerDeceleration)

Upward Decelerating: Outer UIScrollView <== Nested UIScrollView

For the Nested UIScrollView: We constructed the deceleration, and send it out!

    var nestedDeceleration: ScrollingDeceleration?

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        nestedDeceleration = ScrollingDeceleration(velocity: velocity, decelerationRate: scrollView.decelerationRate)
    }

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        nestedDeceleration = nil
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        nestedDeceleration = nil
    }

And pass the nestedDeceleration out to the Outer UIScrollView

And for the Outer UIScrollView:

    let outerScrollingDecelerator = ScrollingDecelerator(scrollView: nestedScrollView)

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        outerScrollingDecelerator.invalidateIfNeeded()
    }

When the Outer UIScrollView got the nestedDeceleration, we can continue the upward decelerating by:

    souterScrollingDecelerator.decelerate(by: nestedDeceleration)

Ref