理解UIView.animate与完成闭包,如何快进而非回退中断动画?
Great question! This is a super common scenario when you want to skip straight to the end of an animation without rolling back any progress made so far. Let’s break down how to pull this off, using Core Animation (the engine behind UIView.animate) to our advantage.
Quick Background Recap
First, remember that UIView.animate is just a friendly wrapper around Core Animation’s implicit animations. When you start an animation, it creates animated properties on the view’s underlying CALayer, and the completion closure fires once those layer animations wrap up—either normally, or if they’re canceled early.
Method 1: Crank Up Layer Speed for Instant Completion
The cleanest approach is to jack up the animation speed on the view’s layer to an absurdly high value. This makes the remaining animation duration effectively zero, triggering the completion closure with finished = true (just like a normal, on-time finish).
Here’s a working example:
// Start a 5-second animation moving a view UIView.animate(withDuration: 5.0, animations: { self.myAnimatedView.frame = CGRect(x: 300, y: 300, width: 150, height: 150) }, completion: { finished in print("Animation done! Finished state: \(finished)") }) // Call this function to fast-forward the animation func fastForwardAnimation() { guard let layer = myAnimatedView.layer else { return } // Set speed to 1000x to finish the animation instantly layer.speed = 1000.0 // Reset speed after a tiny delay so future animations work normally DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { layer.speed = 1.0 } }
Why this works:
- Core Animation scales animation time relative to the layer’s
speedproperty. A speed of 1000 means the animation runs 1000x faster—so a 5-second animation finishes in 0.005 seconds. - The completion closure gets
finished = truebecause the animation completed "normally" (just way faster), not because it was canceled. - No need to track the animation’s target state manually—Core Animation automatically moves the view to its final position.
Method 2: Manual State Jump + Animation Cleanup
If you need more control (like jumping to a specific intermediate state before finishing), you can manually set the view to its target state, wipe the active animation, and trigger the completion logic directly.
Example implementation:
// Predefine the target state to avoid repeating code let targetFrame = CGRect(x: 300, y: 300, width: 150, height: 150) // Start the animation with a reusable completion handler UIView.animate(withDuration: 5.0, animations: { self.myAnimatedView.frame = targetFrame }, completion: handleAnimationCompletion) // Reusable completion logic func handleAnimationCompletion(_ finished: Bool) { print("Animation done! Finished state: \(finished)") // Add any post-animation work here (like enabling a button) } // Fast-forward function func fastForwardAnimation() { // Jump directly to the target state myAnimatedView.frame = targetFrame // Remove active animations to stop any leftover state updates myAnimatedView.layer.removeAllAnimations() // Manually trigger the completion with `finished = true` handleAnimationCompletion(true) }
Pros & Cons:
- Pros: Total control over the final state—you can even adjust the target mid-animation if needed.
- Cons: Requires you to track the animation’s target state explicitly, which gets messy for complex animations (like animating multiple properties at once).
Key Notes to Remember
- If you have multiple animations on the same view, adjusting the layer’s
speedwill affect all of them—keep that in mind if you’re animating separate properties. - For chained animations (starting a new animation inside a completion closure), fast-forwarding the initial animation will automatically trigger the next one, just like a normal finish.
- Never call
layer.removeAllAnimations()without setting the view to its target state first—this will snap the view back to its pre-animation position, which is exactly what you’re trying to avoid!
内容的提问来源于stack exchange,提问作者Nerdy Bunz

