Chain multiple UIView animations without endlessly nesting in completion closures.

Overview

AnimationPlanner

Chain multiple UIView animations without endlessly nesting in completion closures. Used in some of the more superfluous animations in the OK Video app. Very useful with @warpling‘s CAMediaTimingFunction extensions, giving you all the animation curves you need.

📖 Read the documentation to get you up to speed, or read on on to see a little example.

How do I do this?

📦 Add AnimationPlanner to your project (only SPM is currently officially supported) and use the UIView.animateSteps() method to start adding steps to the provided sequence, like shown below.

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

The above code creates the following animation. For more examples see the Sample App available when cloning the repo.

Note: The example shows the custom extension methods for CAMediaTimingFunction mentioned in the intro

Installation

🛠 Adding AnimationPlanner as a package dependency

  1. Go to File -> Add Packages
  2. Paste https://github.com/PimCoumans/AnimationPlanner in the search bar and click on "Add Package"
  3. Select the target(s) in which you want to use AnimationPlanner

📦 Swift Package Manager

Manually add AnimationPlanner as a package dependency in package.swift, by updating your package definition with:

  dependencies: [
    .package(name: "AnimationPlanner", url: "https://github.com/PimCoumans/AnimationPlanner.git", .branch("main"))
  ],

And updating your target‘s dependencies property with dependencies: ["AnimationPlanner"]

🔮 Future plans

While this API removes a lot of unwanted nesting in completion closures when using traditional UIView.animate... calls, a project is never finished and for future versions I have the following plans:

  • Cancel running animation sequences. Current idea is returning a RunningAnimation object with a cancel() method and information about the state of the animation sequence.
  • Remove usage of inaccurate DispatchQueue.main.asyncAfter for manually adding delays where needed.
  • Tidy the API even more by using Swift‘s builder pattern, like we see used in SwiftUI. In practice this would mean removing one more indent in your code!
  • Maybe even allow this package to be used with SwiftUI? No idea how that would work.

Got any feedback or suggestions? Please let me know! ✌🏻

twitter.com/pimcoumans

Comments
  • Deprecate `animateSteps` - Phase 2

    Deprecate `animateSteps` - Phase 2

    ‘Old’ API now uses newer result builder logic behind the scenes. All tests now use new API albeit implicitly.

    • added more tests to debug failing complexer tests
    • renamed a lot or protocols to use Animatable instead of Animates
    • completed RunningAnimation implementation
    opened by PimCoumans 0
  • RunningSequence

    RunningSequence

    Add a RunningSequence class that keeps state of and actually performs all animations (skipping the old AnimationSequence). Adds a onComplete method that now handles a completion handler and even a stopAnimations() method that stops current and cancels future animations.

    Also all protocols have been renamed with more conventional names, creating a much clearer code structure

    opened by PimCoumans 0
  • Result builders phase 1

    Result builders phase 1

    Adding functionality to use result builders to plan your animations, resulting in a cleaner looking experience.

    • Phase 1: New result builder API, lives next to ‘old' API
    • Phase 2: Old API uses result builder structs behind the scenes
    • Phase 3: Deprecate old API and fully move over to result builder API

    Phase 1 introduces the new AnimationPlanner.plan API that lives alongside the current, or ‘old’ UIView.animateSteps method. Behind the scenes the result builder logic transforms the animations to the current implementation of actually performing these animations. The functionality should at least be on par with the current implementation, making sure no animations that should only be performed in a group to appear in a sequence and vice versa.

    • [x] Build out new API, on par with current implementation
    • [x] Separate out tests for both APIs
    • [x] Write documentation for new API
    opened by PimCoumans 0
  • Spring based animations

    Spring based animations

    Adding methods to add spring-based animations to sequences. Using UIView.animate(duration:delay:withSpringDamping:initialVelocity:) method instead of the standard animation method.

    opened by PimCoumans 0
  • Documentation improvements

    Documentation improvements

    Added an actual DocC documentation catalog to the package, giving the package’s documentation an intro text and even a page showing and explaining example code.

    opened by PimCoumans 0
  • Noop animations

    Noop animations

    Fixes #2. Animations that don’t actually change any animatable properties are ignored by UIView.animate, which means any delay that should incur is skipped as well. When finishing an animation, the actual duration is checked and when completed too soon the system now uses DispatchQueue to wait for the appropriate time.

    opened by PimCoumans 0
  • No-op animations ignore delay

    No-op animations ignore delay

    Issue

    When no actual value is changed in an animation, the delay leading up to that animation is ignored as well considering nothing actually needs to animate.

    Proposed solution

    1. Add test to confirm specific issue
    2. Make sure actual animation has taken place (no good idea how yet), use DispatchQueue.asyncAfter() when delay needs to be manually added
    bug 
    opened by PimCoumans 0
  • *Actually* stopping animations

    *Actually* stopping animations

    Notes to follow, but this PR fixes #17 and totally changes how animations are performed. Sub-animations (like those containing within a Group or a Sequence) are now cancelled as well.

    opened by PimCoumans 0
  • Support 120 fps animations

    Support 120 fps animations

    As AnimationPlanners knows exactly how long animations take, a CADisplayLink can be created for its duration set with the max framerate. Animations can also have a property to manually enable this behavior.

    Source: https://github.com/duraidabdul/FrameRateRequest

    opened by PimCoumans 0
  • Cancelling sequence not working for complexer animations

    Cancelling sequence not working for complexer animations

    Problem

    Any animation containing multiple other animations aren’t passing the cancelling on to their sub-animations, only flat sequences are cancelled.

    Proposed solution

    Add func cancel() to Animatable (or a more appropriate protocol) so every animation can handle it for their specific case. ~Maybe create default implementation for Animation where it just immediately executes changes again, but making sure RunningSequence only calls this method when it’s the currently running animation.~ For years now (who new) UIView animations are ‘additive’, meaning they can’t be cancelled by performing the same changes as the animation. Either the currently running animations are played out and subsequent animations are cancelled, or a UIViewPropertyAnimators runs for each animations that can be cancelled.

    opened by PimCoumans 0
Releases(1.0.0)
  • 1.0.0(Jun 24, 2022)

    • Removed any trace of old-style api, everything is result builders moving forward
    • Reorganized file structure. Clearer folder and filenames for all protocols and structs.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.3(Jun 20, 2022)

    Warning: [breaking change] The completion parameter has been removed for theAnimationPlanner.plan and .group methods. Read on how to set your completion handler

    AnimationPlanner now uses the new structs for all animations created through its API. Even the (now deprecated) UIView.animateSteps method results in a sequence animation handled by Animate and its friends.

    To keep tabs on running animations, the RunningSequence class can be used. It is returned from the AnimationPlanner.plan and .group methods. RunningSequence shows the current state, all animations added, but also has a .stopAnimations() method to stop the current and cancel queued animations.

    Use onComplete(_ handler: (_ finished: Bool) -> Void)) on the returned RunningSequence to perform logic when the sequence completes (or is stopped). Because the class is returned when creating your animation sequence, adding a completion handler can be done just like with a trailing closure:

    AnimationPlanner.plan {
        // your animations
    }.onComplete { finished in
        print(“finished!”)
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 0.9.2(Jun 16, 2022)

  • 0.9.1(Jun 16, 2022)

  • 0.9.0(Jun 15, 2022)

    Adding functionality to use result builders to plan your animations, resulting in a cleaner looking experience.

    Phase 1 of this change introduces the new AnimationPlanner.plan API that lives alongside the current, or ‘old’ UIView.animateSteps method. Behind the scenes the result builder logic transforms the animations to the current implementation of actually performing these animations.

    • Phase 1: New result builder API, lives next to ‘old' API
    • Phase 2: Old API uses result builder structs behind the scenes
    • Phase 3: Deprecate old API and fully move over to result builder API
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Jun 8, 2022)

    Introducing the extra step where preparations can be performed or side-effects handled (like triggering haptic feedback). More info in the docs and the complex animation in the sample app.

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Jun 7, 2022)

    Introduces methods to add spring-based animations to your sequence. (also fixes the sample app referring to the package in the wrong location)

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Jun 7, 2022)

  • 0.5.0(Jun 6, 2022)

  • 0.4.0(Jun 4, 2022)

  • v0.3(Jun 2, 2022)

  • 0.2.1(Jun 1, 2022)

  • 0.2(Jun 1, 2022)

  • 0.1(Jun 1, 2022)

Owner
Pim
iOS/macOS dev by day, indie game developer also by day
Pim
⛓ Easy to Read and Write Multi-chain Animations Lib in Objective-C and Swift.

中文介绍 This project is inspired by JHChainableAnimations! Why Choose LSAnimator & CoreAnimator? You can write complex and easy-to-maintain animations in

木子 1.6k Nov 22, 2022
Swiftui-animation-observer - Track SwiftUI animation progress and completion via callbacks

SwiftUI Animation Observer Track SwiftUI animation progress and completion via c

Gordan Glavaš 9 Nov 5, 2022
DaisyChain is a micro framework which makes UIView animations chaining dead simple.

DaisyChain DaisyChain is a micro framework which makes UIView animations chaining dead simple. It uses the exact same interface you are familiars with

Ali Karagoz 31 Nov 3, 2022
A UICollectionViewLayout subclass that adds custom transitions/animations to the UICollectionView without effecting your existing code.

AnimatedCollectionViewLayout Normally a UICollectionView has no transition effects when you scroll from one item to another. There are lots of ways to

Jin Wang 4.5k Jan 1, 2023
Build complex, conducted animations declaratively without manually managing state.

Maestro Build complex, conducted animations declaratively without manually managing state. Code struct AnimatedPieChart: View { private enum Trim

Ryan Wachowski 5 Nov 20, 2022
A collection of animations for iOS. Simple, just add water animations.

DCAnimationKit A collection of animations for iOS Simply, just add water! DCAnimationKit is a category on UIView to make animations easy to perform. E

Dalton 797 Sep 23, 2022
(Animate CSS) animations for iOS. An easy to use library of iOS animations. As easy to use as an easy thing.

wobbly See Wobbly in action (examples) Add a drop of honey ?? to your project wobbly has a bunch of cool, fun, and easy to use iOS animations for you

Sagaya Abdulhafeez 150 Dec 23, 2021
(Animate CSS) animations for iOS. An easy to use library of iOS animations. As easy to use as an easy thing.

wobbly See Wobbly in action (examples) Add a drop of honey ?? to your project wobbly has a bunch of cool, fun, and easy to use iOS animations for you

Sagaya Abdulhafeez 150 Dec 23, 2021
Garland View seamlessly transitions between multiple lists of content.

Garland View seamlessly transitions between multiple lists of content. We specialize in the designing and coding of custom UI for Mobile

Ramotion 504 Jul 31, 2022
UIView category that adds shake animation

UIView category that adds a shake animation like the password field of the OSX login screen. Screenshot Setup with CocoaPods Add pod 'UIView+Shake' to

Andrea Mazzini 498 Nov 20, 2022
UIView subclass that bends its edges when its position changes.

AHKBendableView BendableView is a UIView subclass that bends its edges when its position change is animated. Internally, BendableView contains CAShape

Arek Holko 591 Jul 24, 2022
Twinkle is a Swift and easy way to make any UIView in your iOS or tvOS app twinkle.

Twinkle ✨ Twinkle is a Swift and easy way to make any UIView in your iOS or tvOS app twinkle. This library creates several CAEmitterLayers and animate

patrick piemonte 600 Nov 24, 2022
DynamicBlurView is a dynamic and high performance UIView subclass for Blur.

DynamicBlurView DynamicBlurView is a dynamic and high performance UIView subclass for Blur. Appetize's Demo Since using the CADisplayLink, it is a hig

Kyohei Ito 929 Jan 5, 2023
MGFlipView allows to create flipping view in easy way without worrying about flipping animation and flipping logic.

MGFlipView About If you are looking for an easy way of implement 3D flipping view, you are in the right place. MGFlipView allows to create flipping vi

Maciej Gomółka 47 Sep 28, 2022
Protoyping-Project WallAngle - With this App, you can measure the angle without searching for tools and calculations

Protoyping-Project_WallAngle This is the App that I made for the Prototyping Pro

null 1 Apr 4, 2022
Get a remote image's size and type without downloading the full image

FastImage FastImage 2.0 is an Swift port of the Ruby project by Stephen Sykes. It's directive is to request as little data as possible (usually just t

Kyle Hickinson 64 Sep 2, 2022
Physics-based animations for iOS, tvOS, and macOS.

Advance An animation library for iOS, tvOS, and macOS that uses physics-based animations (including springs) to power interactions that move and respo

Tim Donnelly 4.5k Dec 29, 2022
Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers

Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers. It's magic. Features Animate a

Robert-Hein Hooijmans 1.3k Nov 17, 2022