2

I'm building a simple rotating circle loading animation. I've got the circle rotating but the container view is also moving downward for the duration of the animation. I can't figure out what could be causing this strange behavior.

Example:

enter image description here

The White border is the Parent View ZStack. The Red border is a VStack that I placed to show this weird animation oddity. The Blue border is a ZStack that holds the two Circles that animate.

Here is the code:

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(self.rotationDegree)
        }
          .frame(width: viewWidth, height: viewWidth)
          .border(Color.blue)
          .animation(Animation.linear(duration: 4.5).repeatForever(autoreverses: false), value: rotationDegree)
          .onAppear() {
              self.animateLoader()
          }
    }
      .border(Color.red)
}

   func animateLoader() {
      self.rotationDegree = .degrees(720)
   }
}

Any idea as to why this is happening and how I can get it to stop? Thanks.

flanker
  • 3,840
  • 1
  • 12
  • 20
James Futures
  • 185
  • 2
  • 12
  • 1
    couldn't reproduce this. Copied the code and it is not moving for me. Maybe its something else that is causing it to move that was not shown? Xcode 14.0.1, iPhone 14 pro, iOS 16.0 – sfung3 Nov 18 '22 at 09:11
  • @sfung3 I thought that could be the case as well. Wouldn't the VStack be moving as well if so? – James Futures Nov 18 '22 at 16:18
  • hard to say without more code. Doesn't seem to be an environment issue tho. – sfung3 Nov 18 '22 at 18:24
  • 1
    Look at this [Q & A](https://stackoverflow.com/q/64566492/1630618) – vacawama Nov 19 '22 at 11:43

2 Answers2

3

I figured it out with the help of vacawama who linked to a thread in the comments of the question. That thread also had an answer with another helpful link as well. Those were essentially the same problem as I was having here.

Here's the short and sweet:

It boils down to implicit vs explicit animations. In my code, here, I'm using an "explicit" animation. While using an "implicit" animation (withAnimation()) prevents the problem.

Apparently this may have something to do with how the View is being animated if the View is being navigated to. As was my case, I'm showing this View upon navigation. So the "navigation animation" was getting mixed in with my explicit animation. And, by using an implicit animation, SwiftUI is smart enough to figure out that the navigation animation is not supposed to be part of it.

Here's the code:

var body: some View {
    ZStack {
        Circle()
          .stroke(style: StrokeStyle(lineWidth: backgroundCircleLineWidth))
          .fill(Global.Colors.primary60)
        
        Circle()
          .trim(from: 0, to: 0.15)
          .stroke(style: StrokeStyle(lineWidth: foregroundCircleLineWidth, lineCap: .round))
          .fill(Global.Colors.secondary50)
          .rotationEffect(self.rotationDegree)
    }
      .frame(width: viewWidth, height: viewWidth)
      .onAppear() {
          DispatchQueue.main.async {
              withAnimation(Animation.linear(duration: 2.5).repeatForever(autoreverses: false)) {
                  self.rotationDegree = .degrees(720)
              }
          }
      }
}

I just run the animation inside a withAnimation() block. And it now works as expected.

James Futures
  • 185
  • 2
  • 12
2

For me this suddenly started happening out of nowhere. Hadn't changed any code, but the parent view was suddenly animating slowly. The solution for me was to wrap my animations in a DispatchQueue.main.async { }:

.onAppear {
    DispatchQueue.main.async {
        withAnimation(.easeInOut(duration: Double(Int.random(in: 10..<13))).repeatForever()) {
            blur = 125
        }
    }
}