SwiftUI Shimmer修饰器在iOS 17中失效问题求助
iOS 17 下 Shimmer 修饰器失效的修复方案
问题根源
iOS 17 调整了 SwiftUI 的动画触发逻辑,原代码中通过给AnimatedMask直接附加.animation(animation)的隐式动画方式不再生效,需要改为显式触发动画的写法。
修复后的完整代码
import SwiftUI public struct Shimmer: ViewModifier { let animation: Animation @State private var phase: CGFloat = 0 public init(animation: Animation = Self.defaultAnimation) { self.animation = animation } public static let defaultAnimation = Animation.linear(duration: 1.5).repeatForever(autoreverses: false) public init(duration: Double = 1.5, bounce: Bool = false, delay: Double = 0) { self.animation = .linear(duration: duration) .repeatForever(autoreverses: bounce) .delay(delay) } public func body(content: Content) -> some View { content .modifier(AnimatedMask(phase: phase)) .onAppear { // 显式触发动画 withAnimation(animation) { phase = 0.8 } } .onDisappear { // 重置状态避免复用问题 phase = 0 } } struct AnimatedMask: AnimatableModifier { var phase: CGFloat = 0 var animatableData: CGFloat { get { phase } set { phase = newValue } } func body(content: Content) -> some View { content .mask(GradientMask(phase: phase).scaleEffect(4)) } } struct GradientMask: View { let phase: CGFloat let centerColor = Color.black let edgeColor = Color.black.opacity(0.3) @Environment(\.layoutDirection) private var layoutDirection var body: some View { let isRightToLeft = layoutDirection == .rightToLeft LinearGradient( gradient: Gradient(stops: [ .init(color: edgeColor, location: phase), .init(color: centerColor, location: phase + 0.1), .init(color: edgeColor, location: phase + 0.2) ]), startPoint: isRightToLeft ? .bottomTrailing : .topLeading, endPoint: isRightToLeft ? .topLeading : .bottomTrailing ) } } } public extension View { @ViewBuilder func shimmering( active: Bool = true, duration: Double = 1.5, bounce: Bool = false, delay: Double = 0 ) -> some View { if active { self.modifier(Shimmer(duration: duration, bounce: bounce, delay: delay)) } else { self } } @ViewBuilder func shimmering(active: Bool = true, animation: Animation = Shimmer.defaultAnimation) -> some View { if active { self.modifier(Shimmer(animation: animation)) } else { self } } }
关键修改点
- 移除了
AnimatedMask后的.animation(animation)调用,改为在onAppear中用withAnimation(animation)包裹phase的状态变更,显式触发动画流程,符合iOS17的动画规则。 - 添加
onDisappear重置phase为0,避免视图复用(比如List中的单元格)时出现动画异常。
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

