Chain multiple UIView animations without endlessly nesting in completion closures. Very useful with @warpling‘s CAMediaTimingFunction extensions, giving you all the animation curves you need.

How do I do this?

Add AnimationPlanner to your project (through ? SPM preferably) and use the UIView.animateSteps() method to start adding steps to the provided sequence, like shown below.

UIView.animateSteps { sequence in
        .add(duration: 0.5, timingFunction: .quartOut) {
            view.alpha = 1
   = self.view.bounds.midY
        .add(duration: 0.32, timingFunction: .quintOut) {
            view.transform = CGAffineTransform(scaleX: 2, y: 2)
            view.layer.cornerRadius = 40
            view.backgroundColor = .systemRed
        .add(duration: 0.12, timingFunction: .backOut) {
            view.backgroundColor = .systemBlue
            view.layer.cornerRadius = 0
            view.transform = .identity
        .add(duration: 0.2, timingFunction: .circIn) {
            view.alpha = 0
            view.transform = .identity
            view.frame.origin.y = self.view.bounds.maxY
} completion: { finished in

The above code creates the following animation. For more examples see the included sample app.


Adding AnimationPlanner as a package dependency

  1. Go to File -> Add Packages and select your Project
  2. Paste in the search bar and click on “Add Package”
  3. Select the target(s) in which you want to use AnimationPlanner

Swift Package Manager

Add AnimationPlanner as a package dependency, like shown in the example below:

// swift-tools-version:5.6

import PackageDescription

let package = Package(
  name: "YourProject",
  platforms: [
  dependencies: [
    .package(name: "AnimationPlanner", url: "", .branch("main"))
  targets: [
    .target(name: "YourProject", dependencies: ["AnimationPlanner"])

Future stuff

While this API removes a lot of unwanted nesting in completion closures with traditional UIView.animate... calls, it could be tidied even more by using Swift‘s function builders, like we see used in SwiftUI. For a future release I‘m planning to refactor the code make use of that, hopefully making it look and work a little better.

Got any feedback? Please let me know! ✌?


View Github