You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI动画问题:容器视图为何随旋转动画下移?

问题成因
  1. 全局动画捕获布局变化:你将.animation修饰符应用在了整个承载Circle的ZStack上,当带lineCap: .round的修剪Circle旋转时,其圆角端点会导致视图的内在内容尺寸(intrinsic content size)产生细微变化,这个变化被全局动画捕获,从而引发容器的位移动画。
  2. 未明确布局对齐规则:外层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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:35:24