SwiftUI animation system is simply amazing. But when using gradient fills, it is impossible to animate color change by just changing its color properties. In this article, I will present and discuss possible ways how to animate gradient fills and also address the issue with .hueRotation
modifier.
Options
The urge of animating gradient fill was actually the first topic I was forced to solve when I jumped into SwiftUI. In my app, I wanted to have a gradient background that changes its color from time to time. Here are options how to animate gradient that I am aware of, the first two are very limited the latter two more robust, so choose depending on your use case
- change gradient starting and ending points
- use
.hueRotation
,.saturation
and.brightness
modifiers - create AnimatableModifier
- alpha blend two gradients
Changing gradient starting and ending points
With changing gradient starting and ending points you are able to change gradient orientation and scale. Since the starting and ending point does not have to be limited to view bounds only (0..1) it is possible to stretch gradient beneath view dimension. This can be utilized for color change animation within a pre-defined color set. Better than words is to demonstrate that with example. Below you can see that I have defined gradient with three color stops and by changing starting and ending point I am performing animated color change from red->purple to purple->orange.
Using hueRotation modifier
Using .hueRotation
is the easiest way of gradient alterations. It offers animations in a whole variety of color hues but does not let you change color randomly.
More color changes can be achieved by combination with other modifiers like .brightness
or .saturation
, but they still reflect the source colors and their mutual difference. Next example features usage of hueRotation modifier together with decreasing saturation.
While playing with hueRotation modifiers, I have noticed that it works differently than one would expect. This modifier does not preserve brightness and saturation as it is common in graphical tools (like PS). Here you can see a comparison of results of hueRotation modifier and the expectation via constructing the color directly in HSB space. I am not sure whether this behavior is intended or not, but it is important to be aware of it the result of hueRotation may disappoint you.
Using AnimatableModifier
It is possible to create an animated gradient via AnimatableModifier. This approach was presented by Javier (Kudos!!) in his article at SwiftUI Lab. This approach requires the implementation of color interpolation and its usage of AnimatableModifier reminds me of GeometryEffect (which I have dedicated my previous post to). We have full control of how interpolated value is translated to the view appearance so we can reach really crazy effects like pulsating color transition. This is demonstrated on the following example where interpolation value is altered to growing sine curve according to formula x+sin(8.5Pix)*0.1
Please note that AnimatableModifier actually produces a view, so even that it has been applied to a circle view, our result is still a rounded rectangle.
The main drawback of this approach (IMO) is that you need to provide interpolating value to the effect and it is quite challenging to wrap everything to a simple interface with the single color setter.
Blending of two gradient layers
The solution that I like the most is the simple blending of two gradient layers. It is a sort of workaround but gives you the greatest freedom, it is easy to wrap with a meaningful interface and since the blending views can be actually of any type, you can even create transitions between different types of the gradient (like linear->radial) The example demonstrates this approach, color changes are random
Did you like this article?
Feel free to comment or criticize so the next one is even better. Or share it with other SwiftUI adopters ;)