Wave is a spring-based animation engine for iOS that makes it easy to create fluid, interruptible animations that feel great.

Overview

Wave

Wave is a spring-based animation engine for iOS and iPadOS. It makes it easy to create fluid, interactive, and interruptible animations that feel great.

Wave has no external dependencies, and can be easily dropped into existing UIKit-based projects and apps.

The core feature of Wave is that all animations are re-targetable, meaning that you can change an animation’s destination value in-flight, and the animation will gracefully redirect to that new value.

Understanding Retargeting

Consider these demos of the iOS Picture-in-Picture feature. The screen on the left is created with standard UIKit animations, and the one on the right is created with Wave.

Though both are “interruptible”, the Wave-based implementation handles the interruption much better, and fluidly arcs to its new destination. The UIKit animation feels stiff and jerky in comparison.

At its core, retargeting is the process of preserving an animation’s velocity even as its target changes, which Wave does automatically.

Demo

Installation

Add Wave to your app's Package.swift file, or selecting File -> Add Packages in Xcode:

.package(url: "https://github.com/jtrivedi/Wave")

If you clone the repo, you can run the sample app, which contains a few interactive demos to understand what Wave provides.

Documentation

There’s a full Wave documentation site available for full API and usage documentation.

Getting Started

There are two ways you can interact with Wave, depending on your needs: the block-based and property-based animations:

Block-Based Animation

The easiest way to get started is by using Wave’s block-based APIs that resemble the UIView.animateWithDuration() APIs.

This API lets you animate several common UIView and CALayer properties, like frame, center, scale, backgroundColor, and more.

For these supported properties, Wave will create, manage, and execute the required spring animations under-the-hood.

For example, animating the above PiP view to its final destination is extremely simple:

if panGestureRecognizer.state == .ended {

    // Create a spring with some bounciness. `response` affects the animation's duration.
    let animatedSpring = Spring(dampingRatio: 0.68, response: 0.80)

    // Get the gesture's lift-off velocity, and pass it into the Wave animation.
    let gestureVelocity = panGestureRecognizer.velocity(in: view)

    Wave.animate(withSpring: animatedSpring, gestureVelocity: gestureVelocity) {
        // Update animatable properties on the view's `animator` property, _not_ the view itself.
        pipView.animator.center = pipViewDestination     // Some target CGPoint that you calculate.
        pipView.animator.scale = CGPoint(x: 1.1, y: 1.1)
    }
}

Note that at any time, you can retarget the view’s center property to somewhere else, and it’ll gracefully animate.

Supported Animatable Properties

The block-based API currently supports animating the following properties. For other properties, you can use the property-based animation API below.

  • frame
  • bounds
  • center
  • origin
  • alpha
  • backgroundColor
  • cornerRadius
  • scale
  • translation
  • shadowColor/radius/offset

Upcoming properties:

  • rotation
  • shadow color

Property-Based Animation

While the block-based API is often most convenient, you may want to animate something that the block-based API doesn’t yet support (e.x. rotation). Or, you may want the flexibility of getting the intermediate spring values and driving an animation yourself (e.x. a progress value).

For example, to draw the orange path of the PiP demo, we need to know the value of every CGPoint from the view’s initial center, to its destination center:

// When the gesture ends, create a `CGPoint` animation from the PiP view's initial center, to its target.
// The `valueChanged` callback provides the intermediate locations of the callback, allowing us to draw the path.

let positionAnimator = Animation<CGPoint>(spring: animatedSpring)
positionAnimator.value = pipView.center       // The presentation value
positionAnimator.target = pipViewDestination  // The target value
positionAnimator.velocity = gestureVelocity

positionAnimator.valueChanged = { [weak self] location in
    self?.drawPathPoint(at: location)
}

positionAnimator.start()
Completion Blocks

Both the block-based and property-based APIs support completion blocks. If an animation completes fully, the completion block’s finished flag will be true.

However, if an animation’s target was changed in-flight (“retargeted”), finished will be false, while retargeted will be true.

Wave.animate(withSpring: Spring.defaultAnimated) {
    myView.animator.backgroundColor = .systemBlue
} completion: { finished, retargeted in
    print(finished, retargeted)
}

Example Code

Exploring the provided sample app is a great way to get started with Wave.

Simply open the Wave-Sample Xcode project and hit “Run”. The full source code for the Picture-in-Picture demo is available there, too!

Acknowledgements

Special thanks to Ben Oztalay for helping architect the underlying physics of Wave!

Comments
  • macOS version

    macOS version

    I don't know how feasible this would be as you use a lot of UIKit, but what about some level of AppKit support? I don't mean Catalyst support as I imagine that may work out of the box.

    Maybe next week Apple will announce something to better bridge the gap.

    If this is something you don't ever want to support, feel free to close this. Awesome work!

    opened by erikng 6
  • Should ubiquitous extensions be made public?

    Should ubiquitous extensions be made public?

    Right now, the library ships with a few public extensions of built-in types that are probably already implemented by clients. For example + and - operators on CGPoint. These extensions will compete with any in-house extension a client defines itself in a standalone helper library, and could cause friction at best, or undefined behaviors at worse.

    Of course, these extensions are very nice, but is this the purpose of this library to provide such functionality? If these extensions are not required for the public API of the library (I haven't experimented with it yet), I suppose they should be kept internal.

    opened by tgrapperon 2
  • Changed minimum deployment target to iOS 12.0 (was 13.0)

    Changed minimum deployment target to iOS 12.0 (was 13.0)

    Wave currently does not appear to require iOS 13 APIs. I integrated this change in an app with a minimum deployment target of iOS 12 and tested it on an iPhone 5s running iOS 12.5.5, using both the block-based and the property-based API, and found no issue.

    Allowing iOS 12 instead of 13 means that client apps don't need to make the following models prematurely obsolete: iPhone 5s, iPod touch 6th generation, iPad mini 2, iPad mini 3, iPad Air, 9.7-inch iPad 6th generation.

    opened by Clafou 1
  • Support for macOS and AppKit

    Support for macOS and AppKit

    This PR adds macOS as a supported platform for Wave, and enables first-class animation support for AppKit.

    AppKit apps can now use both the block-based and property-based animation APIs. There's also a tiny sample app for reference, which I'll add more to in the future.

    For example, you can now do this:

    let button = NSButton()
    // configure it...
    
    Wave.animate(withSpring: Spring(dampingRatio: 0.8, response: 1.2)) {
      button.layer?.animator.cornerRadius    = 24.0
      button.layer?.animator.backgroundColor = NSColor.systemGreen.cgColor
      button.layer?.animator.opacity         = 0.5
    }
    

    In a separate patch, I'll add NSView support, so you can animate frame, bounds, etc., and not just properties on CALayer.

    opened by jtrivedi 0
  • Implicit animation support for `CALayer`

    Implicit animation support for `CALayer`

    This patch does a number of things

    • Adds support for animating properties on CALayers that are not backing a UIView.
    • Adds support for animating shadowColor, opacity, borderWidth, and borderColor on CALayer
    • Creates a more readable "header" file that lists the supported animatable properties on UIView and CALayer
    • Removes some (currently) unused code like relativePriority, which was added for affine rotation support
    • Adds some tests for the new animatable properties
    opened by jtrivedi 0
  • Improve backgroundColor animation retargeting #19

    Improve backgroundColor animation retargeting #19

    This PR re-architects how Wave does backgroundColor interpolation. Now, we decompose colors into their components, and animate each component on its own spring.

    This allows colors to be correctly re-targeted in-flight, fixing any jittering/flashing.

    opened by jtrivedi 0
  • Improve `backgroundColor` animation retargeting

    Improve `backgroundColor` animation retargeting

    The current implementation of animating backgroundColor with the block-based API really isn't correct. We need to fully decompose the initial and target color values into their RGBA components, and animate each one on their own spring. That way, if you retarget a color animation, the carryover velocities will actually be correct.

    opened by jtrivedi 0
  • SwiftUI support: rename `Animation` to `SpringAnimator`

    SwiftUI support: rename `Animation` to `SpringAnimator`

    This PR fixes https://github.com/jtrivedi/Wave/issues/15 and allows Wave property animators to be used in SwiftUI.

    Previously, Animation conflicted with SwiftUI.Animation, and couldn't be resolved via Wave.Animation.

    It also adds a small SwiftUI + Wave demo (dragging, throwing, and animating a box).

    opened by jtrivedi 0
  • Animation type names conflict with SwiftUI

    Animation type names conflict with SwiftUI

    Animation conflicts with SwiftUI.Animation, and Wave.Animation doesn't resolve correctly, since Wave is also a type name within the module.

    We should probably rename Animation to SpringAnimator or something.

    opened by jtrivedi 0
  • Non-animated updates shouldn't wait until the next turn of the run loop

    Non-animated updates shouldn't wait until the next turn of the run loop

    let v = UIView()
    v.bounds.size = CGSize(width: 50, height: 50)
    v.animator.scale = CGPoint(x: 0.5, y: 0.5)
    
    print(v.frame.size)
    

    This will print (50, 50) instead of (25, 25) until the next turn of the run loop. We shouldn't require the display link to fire to update things non-animatedly.

    opened by jtrivedi 0
  • Animators in SwiftUI

    Animators in SwiftUI

    Really exciting that we're able to use Wave in SwiftUI. Couple of questions:

    • Should animators be held via @State to avoid instantiating new instances each time views are rendered? https://github.com/jtrivedi/Wave/blob/ecf541bf5debd75a20e1d85c97d41e5ef506c12e/Sample%20App/Wave-Sample/Wave-Sample/SwitUIViewController.swift#L14
    • Seems like this works by updating boxOffset and triggering a state change in SwiftUI - I wonder if there's a more performant way to do this? TimelineView perhaps? https://github.com/jtrivedi/Wave/blob/ecf541bf5debd75a20e1d85c97d41e5ef506c12e/Sample%20App/Wave-Sample/Wave-Sample/SwitUIViewController.swift#L16
    opened by matthewcheok 2
  • Allow an animation not to require an explicit restart after the target is reached then updated again.

    Allow an animation not to require an explicit restart after the target is reached then updated again.

    This framework is great and I found it easy to replace Facebook pop – thank you!

    One minor issue I have is that animations automatically stop when the target value is reached, then become inactive. Any further change to the target value is ignored until the animation is explicitly restarted. But an app may not care whether the animation has reached its target and may just want to animate changes by updating the target.

    Facebook pop has a removedOnCompletion property to address this and keep animations alive when set to false. Would it be possible to add something similar?

    opened by Clafou 3
  • HSL color space interpolation for UIColors

    HSL color space interpolation for UIColors

    Optionally using HSL for color interpolation will provide nicer visual results when blending between colors, as compared to RGB. Specifically, this will help reduce muddy mid-interpolation hues.

    opened by jtrivedi 0
  • Animation delays should be interruptible

    Animation delays should be interruptible

    Wave animation delays shouldn't use dispatch_after. Instead, the engine should track delays internally so an animation's delay can be modified after the fact.

    opened by jtrivedi 0
Releases(0.3.0)
  • 0.3.0(Nov 16, 2022)

    Version 0.3.0 adds macOS as a supported platform for Wave, and enables first-class animation support for AppKit.

    AppKit apps can now use both the block-based and property-based animation APIs. There's also a tiny sample app for reference. In a separate patch, I'll add NSView support, so you can animate frame, bounds, etc., and not just properties on CALayer.

    Other improvements/additions:

    • Support for animating CALayer.borderColor/borderWidth
    • Significantly improved color animation (UIColor and NSColor)
    • Fixed a retain cycle/leak

    API compatibility notes:

    • UIView.animator.backgroundColor and CALayer.animator.backgroundColor are both non-null. If you want to animate to a clear/transparent color, animate to UIColor.clear instead of to nil.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Nov 8, 2022)

  • 0.2.0(Nov 7, 2022)

    Version 0.2.0 renames the Animation class to SpringAnimator, which enables Wave to be used with SwiftUI.

    Due to the API renaming, this version is a breaking change. However, there are only two types that have been renamed, so clients shouldn't have too much difficulty updating to the new name.

    Please reach out if you experience any difficulty adopting the new version.

    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Jun 23, 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
Allows a swipe on any part of the screen to start an interruptible pop animation to the previous view

Lazy Pop SwiftUI Swiping on any part of the screen starts an interruptible pop animation to the previous view. Forked from https://github.com/rishi420

Joe Hinkle 150 Dec 18, 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
Swiftui Spring Animations

Swiftui Spring Animations This repository serves as your reference for SwiftUI Spring Animations. It demonstrates use cases for the various types of s

Stream 348 Dec 30, 2022
A Swift library to take the power of UIView.animateWithDuration(_:, animations:...) to a whole new level - layers, springs, chain-able animations and mixing view and layer animations together!

ver 2.0 NB! Breaking changes in 2.0 - due to a lot of requests EasyAnimation does NOT automatically install itself when imported. You need to enable i

Marin Todorov 3k Dec 27, 2022
A SwiftUI implementation of Wave animating shape.

WaveAnimation A SwiftUI implementation of Wave animating shape. Preview Usage import SineWaveShape SineWaveShape(percent: 0.4, strength: 30, frequency

Zerlz 16 Sep 23, 2022
Cool wave like transition between two or more UICollectionView

CKWaveCollectionViewTransition This is a cool custom transition between two or more UICollectionViewControllers with wave-like cell animation. Could b

Cezary Kopacz 1.9k Oct 4, 2022
Fluid - Use a declarative syntax to build your user interface using UIKit like SwiftUI

Fluid Fluid is powered by ResultBuilder and a custom layout engine. You can uses

HZ.Liu 13 Dec 9, 2022
An experiment for using SwiftUI's custom timing Animation to create an orbital-like animation.

Orbital-SwiftUI-Animation An experiment for using SwiftUI's custom timing curve to create an orbital-like animation. How it looks: How it works: Apply

Mostafa Abdellateef 7 Jan 2, 2023
Aplikacja realizowana w ramach rekrutacji na staż Allegro Spring TECH e-Xperience 2022

RepoFinderAllegro Aplikacja realizowana w ramach rekrutacji na staż Allegro Spring TECH e-Xperience 2022 Uruchamianie Do uruchomienia aplikacji potrze

null 0 Jan 9, 2022
A Fast Animation Engine with an Intuitive API

Kinieta An Animation Engine for iOS with an Intuitive API and Readable Code! (Written in Swift 4.0.) Why another? I decided to build an Animation Engi

Michael Michailidis 44 Sep 22, 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
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
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
API to make great custom transitions in one method

AZTransitions Make your modal transition with custom animation. AZTransitions helps you think about creativity, giving specific API methods. Visual Ex

Alexander 418 Aug 4, 2022
Simple Interface Core Animation. Run type-safe animation sequencially or parallelly

Simple Interface Core Animation Sica can execute various animations sequentially or parallelly. Features Animation with duration and delay parallel /

CATS Open Source Softwares 1k Nov 10, 2022
SwiftUI-Text-Animation-Library - Text animation library for SwiftUI

⚠️ This repository is under construction. SwiftUI Text Animation Library Make yo

null 28 Jan 8, 2023
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