Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI multiple animations for same element

I'm new to SwiftUI, but I have a good knowledge about Storyboard.

At the beginning I want to start with some animations, but I have a problem with one of them:

  • I want an image to become the size 50x50 from 0x0 at the beginning.
  • AFTER it is 50x50 it should repeat an animations that makes the size 50x50 -> 40x40 -> 50x50 -> ... forever

I know @State variables, the .onAppear event, repeating animations with the autoreverse bool, but I don't know how to use them to become the best resolution.

I hope that someone can help me.

@State var element_appeared : Bool = false

Image(systemName: "paperplane.fill")
  .resizable()
  .frame(width: element_appeared ? 50 : 0, height: element_appeared ? 50 : 0)
  .onAppear() {
    element_appeared.toggle()
}
  .animation(.easeInOut)

This is how the animation at the beginning works. I think that I have to work on the element_appeared variable, but I really don't know how..

like image 233
HeroCoder Avatar asked Oct 27 '25 18:10

HeroCoder


2 Answers

You can create an enum as you animation has more than two states. Then use DispatchQueue to delay the second animation.

Here is a demo:

enum AnimationState {
    case notAppeared, appeared, animating
}

struct ContentView: View {
    @State private var animationState: AnimationState = .notAppeared
    private let delay: TimeInterval = 1

    var body: some View {
        Image(systemName: "paperplane.fill")
            .resizable()
            .frame(width: imageSize, height: imageSize)
            .onAppear {
                withAnimation(.easeInOut(duration: delay)) {
                    animationState = .appeared
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                    withAnimation(Animation.easeInOut.repeatForever()) {
                        animationState = .animating
                    }
                }
            }
    }

    private var imageSize: CGFloat {
        switch animationState {
        case .notAppeared: return 0
        case .appeared: return 50
        case .animating: return 40
        }
    }
}
like image 101
pawello2222 Avatar answered Oct 29 '25 09:10

pawello2222


Here's an example. I used a 2nd @State to manage the different animations.

@State var showIcon : Bool = false
@State var animateForever: Bool = false

var body: some View {
    Image(systemName: "paperplane.fill")
        .resizable()
        .frame(width: showIcon ? 50 : 0, height: showIcon ? 50 : 0)
        //.opacity(showIcon ? 1.0 : 0.0)
        .scaleEffect(animateForever ? 0.8 : 1.0) // 0.8 == 40/50
        .onAppear() {
            withAnimation(Animation.easeInOut(duration: 1.0)) {
                showIcon.toggle()
            }
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                withAnimation(Animation.easeInOut(duration: 1.0).repeatForever()) {
                    animateForever.toggle()
                }
            }
        }
}
like image 32
nicksarno Avatar answered Oct 29 '25 08:10

nicksarno