async version of UIViewPropertyAnimator addCompletion

124 views Asked by At

UIViewPropertyAnimator has an addCompletion method that lets you provide a block of code to be executed when the animation ends:

func addCompletion(_ completion: @escaping (UIViewAnimatingPosition) -> Void)

When I try to use an animator in a Task and I call that method, I get a warning:

| Consider using asynchronous alternative function

Sure enough, there is an asynchronous alternative function:

func addCompletion() async -> UIViewAnimatingPosition

But I can't make any sense of this. Where's the block that is to be run when the animation completes? In fact, I find that if I call this method, any code that follows isn't even run. Is this just some massive bug in Apple's provision of async/await alternatives?

Here's an artificial example for use in a view controller:

override func viewDidLoad() {
    super.viewDidLoad()
    Task {
        UIViewPropertyAnimator.runningPropertyAnimator(
            withDuration: 2,
            delay: 2,
            options: [],
            animations: {
                self.view.alpha = 0
            }
        ).addCompletion { [weak self] _ in
            self?.view.alpha = 1
        }
    }
}

You will see the warning on the .addCompletion line. How would you rewrite the code so that it still works (the view disappears and then reappears) but the warning goes away?

2

There are 2 answers

1
Cristik On BEST ANSWER

Is this just some massive bug in Apple's provision of async/await alternatives?

This is part of the Objective-C interoperability for methods with completion handlers:

An Objective-C method that is potentially an asynchronous completion-handler method will be translated into an async method when it is either annotated explicitly with an appropriate swift_async attribute (described in the section on Objective-C attributes) or is implicitly inferred when the following heuristics successfully identify the completion handler parameter:

So, what you see is expected behaviour. Agreed, it looks really strange, you might've run in one of those not-so-nice corner cases, but it's just how the compiler translates by default the async-compatible Objective-C functions.

Anyhow, a workaround to get rid of the warning, and still keep the Task, is to wrap the animation within a MainAction.run call. The closure passed to to MainAction.run is non-async, so the warning will go away.

Task {
    await MainActor.run {
        UIViewPropertyAnimator.runningPropertyAnimator(
            withDuration: 2,
            delay: 2,
            options: [],
            animations: {
                self.view.alpha = 0
            }
        ).addCompletion { [weak self] _ in
            self?.view.alpha = 1
        }
    }
}
2
vadian On

Just await the completion and set the alpha value back to 1

  Task {
        await UIViewPropertyAnimator.runningPropertyAnimator(
            withDuration: 2,
            delay: 2,
            options: [],
            animations: {
                self.view.alpha = 0
            }
        ).addCompletion()
        self.view.alpha = 1
    }