In a project I’m working on in SwiftUI I had a need for a refresh button. I wanted it to be a button with feedback to the user about what is going on and I also wanted to limit the users from hammering the button.
I decided that adding an animation where the image on the button would start rotating when it was pressed and stop rotating when the request was done would be a good enough feedback to the users. The button presses would be throttled in combine to refresh only once every 10 seconds.
To start an animation on a button press is no problem in SwiftUI, all you need to do is to use an updating property, in my case I decided to go with a @Published property in my model.
In my RefreshButton I observe the model object and the UI will update accordingly. Or so I thought… I quickly discovered that no combination of changing my isRotating to false in different places or using withAnimation or trying to set my animation to nil would stop the animation.
It appears that once you add a .repeatForever() modifier to your animation, it will – as the name suggests – repeat forever.
What I had to to do instead to fix this behavior was to introduce a second @Published property into my ObservableObject.
So instead of observing the isRotating to trigger the animation on and off I had to introduce a second state to show the rotating image or not, when it’s false I will instead show a non-rotating version of the same image.
I did also experiment with an animation that repeats a number of times and switching that number to 0 when my isRotating is set to false, and while this stops the animation, it won’t stop until it has actually finished animating which in my situation felt like it took too long.
Ideally I would like a way in the future to be able to toggle the animations on and off in a simpler way, perhaps being able to make a .conditionalAnimation() modifier that takes a bool and either adds the animation or not.