1

I need to do following. Start a function that will wait 5 seconds and then execute withAnimation block that will last 3 seconds. In this 3 second period value of a variable opacity should get to 0. I should be able to cancel this function anywhere during the 8 seconds. My code is like this:

@State var opacity: Double = 1.0

    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        withAnimation(.easeInOut(duration: 3)) {
            opacity = 0.0
        }
    }

How can I achieve that this function can be cancellable anytime during the 8 second period. So if the function gets cancelled, opacity gets set to 1.

2 Answers 2

2

The solution is to use a DispatchWorkItem like so:

struct ContentView: View {
    @State var opacity: CGFloat = 1
    @State private var workItem: DispatchWorkItem?
    
    @State var text = 0
    var body: some View {
        VStack {
            
            Text("Opacity")
                .opacity(opacity)
            
            Button("Start animation") {
                workItem = DispatchWorkItem {
                    withAnimation(.easeInOut(duration: 3)) {
                        opacity = 0.0
                    }
                }
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem!)
            }
            
            Button("Cancel Animation") {
                workItem?.cancel()
                opacity = 1
            }
            .padding(.top, 20)
            
        }
        .padding()
    }
}

You can cancel that operation whenever you want but, It's not even needed to accomplish what you want. You could just reset the opacity back to one. It works even without the DispatchWorkItem in my testings, but if you needed a cancellable task, there you have it!

Sign up to request clarification or add additional context in comments.

Comments

1

You can encapsulate the animation inside a DispatchWorkItem, as it helps you encapsulate the work and attach a completion handle or execution dependencies (in your case, a cancellable action).

Start by encapsulating your animation inside a workItem:

let workItem = DispatchWorkItem {
        withAnimation(.easeInOut(duration: 3).delay(5)) {
            self.opacity = 0.0
        }
 }

Then to manage the execution:

workItem.perform()

And finally the execution dependency (in your case a cancellable action):

cancellable = AnyCancellable {
        workItem.cancel()
        self.opacity = 1.0
}

Full code below:

   struct ContentView: View {
    
    @State private var opacity: Double = 1.0
    @State private var cancellable: AnyCancellable?

    var body: some View {
        Circle()
            .foregroundStyle(.red)
            .frame(width: 200, height: 200)
            .opacity(opacity)
        
            .onAppear {
                self.startAnimation()
            }
        
        Button("Cancel") {
            cancelAnimation()
        }
    }
    

    func startAnimation() {
        let workItem = DispatchWorkItem {
            withAnimation(.easeInOut(duration: 3).delay(5)) {
                self.opacity = 0.0
            }
        }

        workItem.perform()

        cancellable = AnyCancellable {
            workItem.cancel()
            self.opacity = 1.0
        }
    }

    func cancelAnimation() {
        cancellable?.cancel()
        opacity = 1.0
    }
}

This way, when the view has appeared, the animation will start after 5 seconds and if the cancel button is tapped, the code inside your workItem will stop its execution and the opacity will set be to 1.

Also, don't forget to import Combine

2 Comments

Thank you for your answer. This works as expected. Really nice to get the answer from someone just starting on SO. Keep doing it . . .
There's really no particular reason I accepted another answer. Thank you again, your solution is perfect. If I could I would accept both answers as best.

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.