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