Fixing weird shadow animation glitch in SwiftUI

My “glow” animation started going wrong when switching tabs in a tab bar.

Published: Nov. 15, 2023
See books

Recently in a work project I was working on a SwiftUI view with prominent part that had “glow effect” animation that I built with the shadow modifier and the standard SwiftUI animations. Looked great. However I noticed that whenever I would switch tab in the UIKit tab bar and return to this screen, the animation would go wrong.

Shadow animation bug glitch in SwiftUI

What was fascinating about it is that it basically always went wrong in the same way. Parts of the glow would appear clipped somehow in alternating pattern. Even though there is no clipping and I used frame to give it minimal size way bigger than the circle to make sure no clipping could occur.

Next was trying to reset the animations on tab switch, which is apparently done by setting the animated properties to their default values and then restarting the animation with withAnimation block. This curiously also did not help. Even when I waited a couple of seconds before starting the animation again.

Of course searching for “glow animation glitch” online went nowhere 😄 Maybe I choose the wrong terms, because I doubt I am the first one to run into issues with animating shadows.

What saved me in the end was the id modifier. I basically created @State property of type UUID that I would use as the .id modifier on the entire view that was being animated. And each time the UIHostingController appeared again which indicated tab bar switching, I would just create new UUID which would force SwiftUI do discard the previously animated view and create a new one, since its id was different.

And boom! Problem solved. I tried really hard to reproduce the issue again but couldn’t.

Here is very simplified version of what I did:

struct MyContainerView: View {

    @State private var animationStateId = UUID()

    var body: some View {
        VStack {
            CustomViewWithGlowAnimation()
            .id(animationStateId)

            // more views...
        }
        .onReceive(NotificationCenter.default.publisher(for: Notification.Name("resetGlowAnimation"), perform: { _ in
            self.animationStateId = UUID()
        })
    }
}

And then I can just use NotificationCenter.default to send the “resetGlowAnimation” notification whenever I need to make sure the animation is animating properly.

If you know about better solution, I am happy to hear it.

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀