Hi! In this article, I would like to tell you how the scrolling of
UIScrollView works, what functions are used and how to implement this mechanics by yourself.
Understanding how the scrolling works can be useful for the implementation of
UIScrollView animation for another view with
UIPanGestureRecognizer for example. Moreover, this mechanics was used in the implementation of the underground scheme scrolling in the multi-platform library of Yandex.Metro for iOS and Android.
It is necessary to find the equation of motion to understand how the scrolling works. Once we find it, we can come up with the rest of the functions: for the scroll time, velocity and for the final position (projection) of the scrolling.
The function of the scroll projection has already been introduced in the 803 Designing Fluid Interfaces (WWDC18) presentation.
But it is just the function of the scroll projection. It is not enough for either the time function or the equation of motion. But it can be used for the reference of our own calculations.
The velocity function
Let’s come up with all the necessary functions and then write them in Swift. First of all we can find the function of velocity. The
.decelerationRate parameter is used for
UIScrollView configuration. It shows how much the scroll speed decelerates over time. It is important to keep in mind that the
UIScrollView parameters are set in milliseconds, not seconds (like
UIGestureRecognizer for example). So the
.decelerationRate shows how much the scroll speed decelerates in one millisecond.
So if we want to use
UIScrollView.DecelerationRate then the function of velocity will look like this:
𝑣 — velocity,
𝑣ₒ — initial velocity in pt/s (points per second),
d — deceleration rate (0 < d < 1),
t — time.
The equation of motion
Now we are ready to find the equation of motion. To do this, we should integrate the velocity function:
x — position,
xₒ — initial position.
As a result, we get the function:
log — natural logarithm.
The final position function
x(t) tends to the final position as t → ∞ because 0 < d < 1:
X — final position.
So we got the final position function but it is not equal to the WWDC function that looks like this in our notation:
Our functions differ in these parts:
Let’s substitute the default
.decelerationRate values from the SDK. If we substitute
.normal = 0.998 we get
499.0, and if we substitute
.fast = 0.99 we get 99.5 and 99.0 respectively.
The difference is not too big. This is because the right function is a consequence of the decomposition of the natural logarithm in the Taylor series using the Euler transform. According to the WolframAlpha, functions behave in a similar way as d tends to 1.
The function that was recommended at WWDC approximates the function we found in the neighborhood of 1, but its calculation is faster. We will continue to use the logarithm in the functions but in writing the code, we will already use a more optimal variant.
The function that was recommended at the conference approximates the function we found in the neighborhood of 1 but its calculation is faster. In the formulas, we will continue to use the logarithm but in the code we will use the optimal variant.
The time function
As a result, we found the equation of motion and the final position function. But we also need the time function to create an animation.
When we were looking for the final position time was tending to infinity. But we cannot use infinite time. But in our case it is not necessary, the function converges to the final position. Therefore, we formulate our problem differently: we find the time when the value of the function is close enough to the final position. Let’s call the difference of values ε value.
Now we need to find the time T which:
From this equation we obtain the necessary T:
As the value of ε, you can use 0.1 (0.1 point) for example.
So we found all the necessary functions. Now it remains to implement Swift functions for them.
Implementation of Swift functions
We will assume that our points and speed are
CGPoint. For convenience, we combine all of our original parameters in the
ScrollTimingParameters structure, renaming ε to the
In the formulas, we used vector arithmetic operations: addition, subtraction and multiplication, and division by number. We also used the length of the vector. There are no such functions for
CGPoint in the SDK, so we need to add them ourselves:
Now we have everything we need to implement the functions. Let’s start with the final position function. We use a more accurate and less optimal formula using a logarithm as an example. If you are sure that the
decelerationRate values in your project are always close to 1, you can replace
(d — 1) / d as in WWDC.
Then we implement the time function:
Finally, the function to calculate intermediate values:
So, we have implemented all the necessary functions to implement the scrolling. As a result, we can create a scroll animation using the
TimerAnimation which calls the given block with the current progress:
You can find all these functions,
TimerAnimation implementation and the Playground example on GitHub.
However, there is not only the scrolling in
UIScrollView but also the bounce and the rubber banding effects. We will explore how they work in one of the next articles.
🇷🇺 This article is also available in Russian.
🚀 Drawer view example (like in Apple Maps or Stocks) on GitHub.