Swift
animateWithDuration
iOS Development
Animation
Completion Handler

Blocks on Swift animateWithDurationanimationscompletion

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UIView.animate is one of the most common APIs for simple iOS animations, and its two closure parameters do different jobs. The animations closure defines the property changes UIKit should animate. The completion closure runs afterward and tells you whether the animation finished naturally or was interrupted. Once you understand that separation, chaining and debugging animations becomes much easier.

The Two Closures Do Different Work

In modern Swift, the API is written as UIView.animate(withDuration:animations:completion:). The first closure changes view properties. UIKit captures those changes and animates from the current state to the new one.

swift
1import UIKit
2
3final class DemoViewController: UIViewController {
4    let box = UIView()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        box.frame = CGRect(x: 40, y: 120, width: 80, height: 80)
9        box.backgroundColor = .systemBlue
10        view.addSubview(box)
11
12        UIView.animate(withDuration: 0.4, animations: {
13            self.box.alpha = 0.3
14            self.box.center.x += 120
15        }, completion: { finished in
16            print("Animation finished: \(finished)")
17        })
18    }
19}

The animations closure should contain property assignments such as alpha, center, transform, or layout changes followed by layoutIfNeeded(). The completion closure is for follow-up work such as enabling buttons, starting another animation, or removing a view.

What the finished Flag Means

The completion closure receives a Bool named finished. This is important.

  • 'true means UIKit reached the intended end state.'
  • 'false means the animation was interrupted or replaced before completing.'

That distinction matters if you are chaining behavior. For example, if you only want to remove a view after a fade-out truly completes, check the flag before deleting anything.

swift
1UIView.animate(withDuration: 0.25, animations: {
2    self.box.alpha = 0
3}, completion: { finished in
4    if finished {
5        self.box.removeFromSuperview()
6    }
7})

Without that check, interrupted animations can leave your UI in inconsistent states.

Animating Auto Layout Changes

For Auto Layout-based interfaces, you usually animate constraint changes rather than raw frames. The pattern is to update constraints first, then call layoutIfNeeded() inside the animation closure.

swift
1self.leadingConstraint.constant = 180
2
3UIView.animate(withDuration: 0.3, animations: {
4    self.view.layoutIfNeeded()
5})

This works because UIKit interpolates the layout change over the animation duration.

Chaining Animations Cleanly

The completion closure is the usual place to start a second animation after the first one succeeds.

swift
1UIView.animate(withDuration: 0.3, animations: {
2    self.box.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
3}, completion: { finished in
4    guard finished else { return }
5
6    UIView.animate(withDuration: 0.2) {
7        self.box.transform = .identity
8    }
9})

For simple sequences, nested closures are fine. For larger animation flows, pull each step into a helper method so the code stays readable.

Capturing self Carefully

Animation closures are usually short-lived, so retain cycles are less dangerous than with long-lived stored callbacks. Even so, if an animation is part of a larger controller lifecycle or a repeating chain, using [weak self] can still be a good habit.

The real goal is not ritual use of weak references. The goal is understanding who should keep whom alive.

Common Pitfalls

  • Putting follow-up logic inside the animations closure instead of the completion closure.
  • Ignoring the finished flag when interruptions matter.
  • Changing Auto Layout constraints without calling layoutIfNeeded() inside the animation block.
  • Animating properties that UIKit does not interpolate the way you expect.
  • Building long chains of nested animations without extracting helper methods.

Summary

  • The animations closure defines what changes should animate.
  • The completion closure runs afterward and receives a finished flag.
  • Use the completion block for follow-up actions and chained animations.
  • For Auto Layout, change constraints first and animate layoutIfNeeded().
  • Keep animation closures focused so UI flow stays readable and predictable.

Course illustration
Course illustration

All Rights Reserved.