darekm
darekm

Reputation: 295

How to keep the initial animation running while a new one is performed?

I'm having a hard time figuring out this animation thing. The problem is, the .onAppear wobbly animation (which should be forever looped) stops and freezes as soon as the view is dragged (which has another animation so that it smoothly moves towards the drag location).

Is there a way to keep the onAppear animation running independently of the view being dragged around? Important to note is that I do need the position to be dependent on the array (hence drag gesture writes directly to data), and I don't need the wobble (size animation) to be tied to the array at all.

Illustration of the animation freezing on drag (undesirable - would like to circles to still wobble during and after being dragged)

circles

Please see code:

   var body: some View {
        
      
            
       
        
        Circle()
           
          //  .resizable()
            .frame(width:size.width, height: size.height) //self.size is a state var
            .foregroundColor(color)
            .position(pos) //pos is a normal var bound to an array row
            .gesture(DragGesture()
            
                        .onChanged { gesture in
                        
                         withAnimation(.default) {
                            ballStorage.balls[numberInArray].position = gesture.location
                            }
                            
                            
                        }
                        .onEnded { gesture in
                          
                             ballStorage.checkOverlaps(for: numberInArray)
                           
                        }
                        
            ) .onAppear {
                
                withAnimation(Animation.easeInOut(duration: 1).repeatForever(autoreverses: true)) {
                    
                    size = CGSize(width: size.width+20, height: size.height+20)
                }
                
            }

        }
}

Upvotes: 1

Views: 292

Answers (1)

Asperi
Asperi

Reputation: 257693

You need to separate circle into own view with own animation, then internal animation will be independent of external animation.

Here is a demo of solution on somehow replicated code. Prepared with Xcode 12.1 / iOS 14.1.

demo

struct Ball: Identifiable, Hashable {
    let id = UUID()
    var position = CGPoint.zero
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

struct BallView: View {
    let ball: Ball
    var color = Color.blue
    @State private var size = CGSize(width: 40, height: 40)
    var body: some View {
        Circle()
            .frame(width:size.width, height: size.height) //self.size is a state var
            .animation(Animation.easeInOut(duration: 1).repeatForever(autoreverses: true), value: size)
            .foregroundColor(color)
            .onAppear {
                size = CGSize(width: size.width+20, height: size.height+20)
            }
    }
}

struct ContentView: View {
    @State private var balls = [Ball(position: CGPoint(x: 100, y: 100)), Ball(position: CGPoint(x: 100, y: 200))]
    @State private var off = CGSize.zero
    @State private var selected = -1
    var body: some View {
        
        ZStack {
            ForEach (Array(balls.enumerated()), id: \.1) { i, ball in
                BallView(ball: ball)
                    .offset(x: i == selected ? off.width : 0, y: i == selected ? off.height : 0)
                    .position(ball.position)
                    .gesture(DragGesture()
                        .onChanged { gesture in
                            self.selected = i
                            withAnimation(.default) {
                                self.off = gesture.translation
                            }
                        }
                        .onEnded { gesture in
                                let pt = balls[i].position
                                self.balls[i].position = CGPoint(x: pt.x + gesture.translation.width, y: pt.y + gesture.translation.height)
                                self.off = .zero
                                self.selected = -1
//                          ballStorage.checkOverlaps(for: numberInArray)
                        }
                    )
            }
        }
    }
}

Upvotes: 1

Related Questions