Swiftpack.co - lkzhao/YetAnotherAnimationLibrary as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
lkzhao/YetAnotherAnimationLibrary
Designed for gesture-driven animations. Fast, simple, & extensible!
.package(url: "https://github.com/lkzhao/YetAnotherAnimationLibrary.git", from: "1.6.0")

Yet Another Animation Library

Designed for gesture-driven animations. Fast, simple, & extensible!

It is written in pure swift 3.1 with protocol oriented design and extensive use of generics.

Consider this as a swift optimized version of facebook's pop. It plays nicer with swift and faster too.

Fast:

  • Uses SIMD types and instructions for calculation
  • Better compiler optimization through swift generics

Simple:

  • Supports Curve(Basic), Spring, & Decay animations out of the box
  • Easy API for animating common animatable properties. (checkout the Extensions folder for list of included properties)
  • Type safety guaranteed when assigning animation values
  • Observable, including value, velocity, and target value
  • Builtin chaining operator to easily react to changes in value
  • Provide velocity interpolation with gestures

Extensible:

  • Supports custom property
  • Supports custom animatable type
  • Supports custom animation

Installation

pod "YetAnotherAnimationLibrary"

Usage

Animation

// Spring animation
view.yaal.center.animateTo(CGPoint(x:50, y:100))
view.yaal.alpha.animateTo(0.5, stiffness: 300, damping: 20)

// Curve(Basic) animation
view.yaal.frame.animateTo(CGRect(x:0, y:0, width:50, height:50), duration:0.5, curve: .linear)

// Decay Animation
view.yaal.center.decay(initialVelocity:CGPoint(x:100, y:0))

Observe Changes

// observe value changes
view.yaal.center.value.changes.addListener { oldVelocity, newVelocity in
  print(oldVelocity, newVelocity)
}
// observe velocity changes
view.yaal.center.velocity.changes.addListener { oldVelocity, newVelocity in
  print(oldVelocity, newVelocity)
}

Chaining Reactions

// when scale changes, also change its alpha
// for example if view's scale animates from 1 to 0.5. its alpha will animate to 0.5 as well
view.yaal.scale.value => view.yaal.alpha
// equvalent to the following
// view.yaal.scale.value.changes.addListener { _, newScale in
//   view.yaal.alpha.animateTo(newScale)
// }

// optionally you can provide a mapping function in between.
// For example, the following code makes the view more transparent the faster it is moving
view.yaal.center.velocity => { 1 - $0.magnitude / 1000 } => view.yaal.alpha
// equvalent to the following
// view.yaal.center.velocity.changes.addListener { _, newVelocity in
//   view.yaal.alpha.animateTo(1 - newVelocity.magnitude / 1000)
// }

Set Value (Notify listeners)

// this sets the value directly (not animate to). Change listeners are called.
// Velocity listeners will receive a series of smoothed velocity values.
view.yaal.center.setTo(gestureRecognizer.location(in:nil))

Advance Usages

React to changes

Animate is very efficient at observing animated value and react accordingly. Some awesome effects can be achieved through observed values.

For example, here is a simple 2d rotation animation thats made possible through observing the center value's velocity.

   override func viewDidLoad() {
     // ...
     view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap(gr:))))
     squareView.yaal.center.velocity => { $0.x / 1000 } => squareView.yaal.rotation
   }
   func tap(gr: UITapGestureRecognizer) {
       squareView.yaal.center.animateTo(gr.location(in: view))
   }

Animate also provide smooth velocity interpolation when calling setTo(_:). This is especially useful when dealing with user gesture.

For example. the following does a 3d rotate animation when dragged

override func viewDidLoad() {
    // ...
    squareView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan(gr:))))
    squareView.yaal.perspective.setTo(-1.0 / 500.0)
    squareView.yaal.center.velocity => { $0.x / 1000 } => squareView.yaal.rotationY
    squareView.yaal.center.velocity => { -$0.y / 1000 } => squareView.yaal.rotationX
}

func pan(gr: UIPanGestureRecognizer) {
    squareView.yaal.center.setTo(gr.location(in: view))
}

Custom property

To animate custom property, just create an animation object by calling SpringAnimation(getter:setter:). Use the animation object to animate and set the values. There are 4 types of animations provided by Animate:

  • SpringAnimation
  • CurveAnimation
  • DecayAnimation
  • MixAnimation (does all three types of animation)
class Foo {
    var volumn: Float = 0.0
    lazy var volumnAnimation: SpringAnimation<Float>
        = SpringAnimation(getter: { [weak self] in self?.volumn },
                          setter: { [weak self] in self?.volumn = $0 })
}

volumnAnimation.animateTo(0.5)

If your class inherits from NSObject, then it is even easier by using the built in animation store.

via extension & yaal.animationFor

extension Foo {
    public var volumnAnimation: MixAnimation<CGRect> {
        return yaal.animationFor(key: "volumn",
                                 getter: { [weak self] in self?.volumn },
                                 setter: { [weak self] in self?.volumn = $0 })
    }
}

via yaal.register & yaal.animationFor

// or register ahead of time
yaal.register(key: "volumn", 
              getter: { [weak self] in self?.volumn },
              setter: { [weak self] in self?.volumn = $0 })

// and retrieve the animation object through the same key.
yaal.animationFor(key: "volumn")!.animateTo(0.5)

// NOTE: that this method have limited type safety. You can basically pass any animatable type into `animateTo()`
// There is nothing to stop you from doing the following. but they will crash at run time
yaal.animationFor(key: "volumn")!.animateTo(CGSize.zero)
yaal.animationFor(key: "volumn")!.animateTo(CGRect.zero)

Custom Animatable Type

Custom animatable types are also supported. Just make the type conform to VectorConvertable.

// the following makes IndexPath animatable
extension IndexPath: VectorConvertible {
    public typealias Vector = Vector2
    public init(vector: Vector) {
        self.init(item: Int(vector.x), section: Int(vector.y))
    }
    public var vector: Vector {
        return [Double(item), Double(section)]
    }
}
// Can now be used like this
let indexAnimation = SpringAnimation(getter: { self.indexPath },
                                     setter: { self.indexPath = $0 })
indexAnimation.animateTo(IndexPath(item:0, section:0))

// Note that everything is type safe. incorrect type won't be allowed to compile

Custom Animation

Just subclass Animation and override update(dt:TimeInterval) method. If your animation need getter & setter support, subclass ValueAnimation instead. Checkout the builtin animations for example.

GitHub

link
Stars: 501
Last commit: 4 weeks ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Submit a free job ad (while I'm testing this). The analytics numbers for this website are here.

Release Notes

4 weeks ago

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics