SwiftUI动画问题:容器视图为何随旋转动画下移?
问题成因
- 全局动画捕获布局变化:你将
.animation修饰符应用在了整个承载Circle的ZStack上,当带lineCap: .round的修剪Circle旋转时,其圆角端点会导致视图的内在内容尺寸(intrinsic content size)产生细微变化,这个变化被全局动画捕获,从而引发容器的位移动画。 - 未明确布局对齐规则:外层VStack默认使用居中对齐,加上内在尺寸的微小波动,进一步放大了位移的视觉效果。
解决方案
方法1:仅给旋转效果添加动画(推荐)
把动画修饰符直接附加到rotationEffect上,确保只有旋转动作被动画化,布局变化不会触发动画:
struct CustomLoadingView: View { let viewWidth: CGFloat = UIScreen.main.bounds.width * 0.5 let backgroundCircleLineWidth: CGFloat = UIScreen.main.bounds.width * 0.025 let foregroundCircleLineWidth: CGFloat = UIScreen.main.bounds.width * 0.02 @State var rotationDegree: Angle = Angle.degrees(0) var body: some View { VStack(spacing: 0) { ZStack { Circle() .stroke(style: StrokeStyle(lineWidth: backgroundCircleLineWidth)) .fill(Global.Colors.primary60) Circle() .trim(from: 0, to: 0.15) .stroke(style: StrokeStyle(lineWidth: 7, lineCap: .round)) .fill(Global.Colors.secondary50) // 将动画仅应用到旋转效果 .rotationEffect(rotationDegree) .animation(Animation.linear(duration: 4.5).repeatForever(autoreverses: false), value: rotationDegree) } .frame(width: viewWidth, height: viewWidth) .border(Color.blue) .onAppear() { animateLoader() } } .border(Color.red) } func animateLoader() { rotationDegree = .degrees(720) } }
方法2:固定视图边界,消除尺寸波动
给修剪后的Circle添加固定尺寸的frame,确保其内在尺寸不会随旋转变化,同时明确VStack的对齐方式:
struct CustomLoadingView: View { let viewWidth: CGFloat = UIScreen.main.bounds.width * 0.5 let backgroundCircleLineWidth: CGFloat = UIScreen.main.bounds.width * 0.025 let foregroundCircleLineWidth: CGFloat = UIScreen.main.bounds.width * 0.02 @State var rotationDegree: Angle = Angle.degrees(0) var body: some View { VStack(spacing: 0, alignment: .center) { // 明确对齐方式 ZStack { Circle() .stroke(style: StrokeStyle(lineWidth: backgroundCircleLineWidth)) .fill(Global.Colors.primary60) Circle() .trim(from: 0, to: 0.15) .stroke(style: StrokeStyle(lineWidth: 7, lineCap: .round)) .fill(Global.Colors.secondary50) .rotationEffect(rotationDegree) .frame(width: viewWidth, height: viewWidth) // 固定尺寸 } .frame(width: viewWidth, height: viewWidth) .border(Color.blue) .animation(Animation.linear(duration: 4.5).repeatForever(autoreverses: false), value: rotationDegree) .onAppear() { animateLoader() } } .border(Color.red) } func animateLoader() { rotationDegree = .degrees(720) } }
内容的提问来源于stack exchange,提问作者James Futures
相关产品推荐
相关产品推荐

