Swift UI Image flickers when animating

Swift UI Image flickers when animating
ios
Ethan Jackson

Im trying to apply a simple shaking effect to an image to simulate a deleting animation. When I do this the image flickers. When I try this with a normal circle or any native swift ui view it works just fine. Why does the image flicker?

I have tried this with Apple's native AsyncImage and the same bug happens.

import SwiftUI import Kingfisher struct PinnedChatView: View { var body: some View { ZStack { VStack(spacing: 8){ KFImage(URL(string: "https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg")) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 100, height: 100) .clipShape(Circle()) .jiggle(isEnabled: true) } } } } extension View { @ViewBuilder func jiggle(amount: Double = 4, isEnabled: Bool = true) -> some View { if isEnabled { modifier(JiggleViewModifier(amount: amount)) } else { self } } } private struct JiggleViewModifier: ViewModifier { let amount: Double @State private var isJiggling = false func body(content: Content) -> some View { content .offset(x: isJiggling ? 3 : -3) .offset(y: isJiggling ? -3 : 3) .animation( .easeInOut(duration: randomize(interval: 0.07, withVariance: 0.025)) .repeatForever(autoreverses: true), value: isJiggling ) .animation ( .easeInOut(duration: randomize(interval: 0.14, withVariance: 0.025)) .repeatForever(autoreverses: true), value: isJiggling ) .onAppear { isJiggling.toggle() } } private func randomize(interval: TimeInterval, withVariance variance: Double) -> TimeInterval { interval + variance * (Double.random(in: 500...1_000) / 500) } }

Answer

Because it is animating the other modifiers like resize, aspectRatio and so on.

A quick workaround for this is to use task modifier instead of onAppear inside the JiggleViewModifier:

.task { isJiggling.toggle() }

Demo


Alternative method

You can also achieve the same result with a little bit of logic change in the body of the JiggleViewModifier:

@State private var x = 0.0 @State private var y = 0.0 func body(content: Content) -> some View { content .offset(x: x, y: y) .onAppear { let animation = Animation .easeInOut(duration: randomize(interval: 0.07, withVariance: 0.025)) .repeatForever(autoreverses: true) withAnimation(animation) { x = amount y = -amount } } }

P.S.: You originally forgot to use the amount parameter.

Related Articles