SwiftUI中图片添加抖动动画时出现闪烁问题求助
解决SwiftUI中图片抖动动画闪烁问题
问题描述
我尝试给图片添加简单的抖动效果模拟删除动画,但图片出现闪烁。用普通圆形或其他SwiftUI原生视图时动画正常,使用Apple原生AsyncImage也会出现同样的闪烁问题,相关代码如下:
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) } }
问题原因
闪烁的核心原因:
- 同一个
isJiggling状态绑定了两个独立动画,导致SwiftUI动画系统冲突,异步加载的图片(如KFImage、AsyncImage)因视图重绘频繁,冲突被放大为闪烁。 randomize方法每次调用都会生成新随机值,动画初始化时的不一致进一步加剧了不稳定。
修复方案
修改后的代码
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()) .animation(nil, value: UUID()) // 隔离图片加载时的动画干扰 .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 // 提前生成随机动画时长,避免每次计算都变化 private let xDuration: TimeInterval private let yDuration: TimeInterval init(amount: Double) { self.amount = amount self.xDuration = randomize(interval: 0.07, withVariance: 0.025) self.yDuration = randomize(interval: 0.14, withVariance: 0.025) } func body(content: Content) -> some View { content .offset(x: isJiggling ? amount : -amount) .offset(y: isJiggling ? -amount : amount) .animation( // 组合x、y轴动画,用不同时长实现自然抖动 Animation.easeInOut(duration: xDuration) .repeatForever(autoreverses: true) .combined(with: Animation.easeInOut(duration: yDuration) .repeatForever(autoreverses: true)), value: isJiggling ) .onAppear { isJiggling.toggle() } } private func randomize(interval: TimeInterval, withVariance variance: Double) -> TimeInterval { interval + variance * (Double.random(in: 0...1) * 2 - 1) // 生成-1到1的随机值,让时长有正负波动 } }
关键修改点
- 合并动画逻辑:用
combined(with:)将x、y轴的动画合并为一个,避免同一状态触发多个动画导致冲突。 - 稳定随机时长:在
init中提前生成动画时长,确保动画过程中参数稳定,减少不必要的重绘。 - 隔离图片加载动画:给KFImage添加
animation(nil),防止图片加载完成时的布局变化干扰抖动动画。 - 优化随机值生成:调整
randomize方法,生成正负波动的随机时长,让抖动效果更自然。
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

