4

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..

2
  • You need to delay second change. Next should be helpful stackoverflow.com/a/65299904/12299030. Commented Dec 23, 2020 at 9:34
  • 1
    Okay, First I want to thank you for your answer, but I'm to new in SwiftUI to understand the code in the other post :( How can I delay the second animation and how can I ignore the first one, after it made it's job? ^^ Commented Dec 23, 2020 at 12:59

3 Answers 3

4

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
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

You can add a .delay() to the second animation, meaning that you don't have to use the DispatchQueue. Cheers.
1

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()
                }
            }
        }
}

1 Comment

You could also leave out the whole DispatchQueue, and just add a .delay(1) to the second animation. Cheers.
0

Based on @nicksarno's answer, replacing use of DispatchQueue, with a .delay() on the second animation:

struct AnimatingPlaneView: View {

    @State private var showIcon = false
    @State private var animateForever = false

    private var sideSize: Double { showIcon ? 50 : 0 }
    
    var body: some View {
        Image(systemName: "paperplane.fill")
            .resizable()
            .frame(width: sideSize, height: sideSize)
            .opacity(showIcon ? 1 : 0)
            .scaleEffect(animateForever ? 0.8 : 1)
            .onAppear {
                withAnimation(.easeInOut(duration: 1)) {
                    showIcon.toggle()
                }
                withAnimation(.easeInOut(duration: 1).repeatForever().delay(1)) {
                    animateForever.toggle()
                }
            }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.