Swift Tweener
Swift animation engine, make more powerful and creative Apps.
This project has rewritten in pure Swift from CocoaTweener
Prerequisites
- Swift 5.0
Features
Declarative & chainable syntax
Now, with Declarative Syntax and Tween chaining, to create a Tween:
Tween(myView)
.duration(1.0)
.ease(.inOutCubic)
.to(
.key(\.alpha, 1.0),
.key(\.frame, CGRect(x:20.0,
y:20.0,
width:UIScreen.main.bounds.width - 40,
height:UIScreen.main.bounds.width - 40)),
.key(\.backgroundColor!, .red)//NOTE:This property is an optional, add ! to keypath.
)
.onComplete { print("Tween complete") }
.after()//Creates a new tween after with same target and properties.
.duration(1.0)
.ease(Ease.outBounce)
.to(
.key(\.alpha, 0.25),
.key(\.frame, CGRect(x:20.0, y:20.0, width:100.0, height:100.0)),
.key(\.backgroundColor!, .blue)
)
.play()
To create a Timeline:
Timeline(
//Place tweens here, separated by commas.
//Tween 1
Tween(myView)
.ease(.inOutQuad)
.to(.key(\.center, .zero) )
.onStart {
self.flipX(inverted: true)
}
.onComplete { print("Tween 1 complete") },
//Tween 2
Tween(myView)
.to(.key(\.center, self.center) )
.onStart {
self.flipY()
}
.onComplete { print("Tween 2 complete") }
//Etc....
)
.play()
View's extensions
To make it more friendly, now includes UIView's and NSView's extensions with predefined animations ready-to-use calling a single function from your view instance:
.spring()
.zoomIn()
.zoomOut()
.pop()
.fadeIn()
.fadeOut()
.flyLeft()
.flyRight()
.flyTop()
.flyBottom()
.slideLeft()
.slideRight()
.slideTop()
.slideBottom()
.flipX()
.flipY()
.shake()
.jiggle()
.bounce()
.swing()
.spin()
.loop()
Any object Type Support
To add support to other Types and custom Types, assuming there is a struct like this:
public struct Vector3{
var x, y, z: Double
func buffer() -> [Double] { return [x, y, z] }
static func zero() -> Vector3 { return Vector3(x:0.0, y:0.0, z:0.0) }
static var random: Vector3 {
return Vector3( x:.random(in: 0...1.0),
y:.random(in: 0...1.0),
z:.random(in: 0...1.0)
)
}
}
Tweener is based on Double arrays so you have to tell it how to convert your object to Array and back to Object.
Tweener.addType(
toType:{ values in return Vector3(x:values[0], y:values[1], z:values[2]) },
toArray:{ point in return point.buffer() }
)
Now, you can animate a 'Vector3' Type object.
Tween(myInstance)
.to(.key(\.myVec3Property, .random))
.play()
MacOS support
This version includes macOS support and samples.
Installation
Install using Cocoapods
To integrate install Cocoa Pods using this gem:
$ gem install cocoapods
Now, add Tweener to your Podfile
pod 'Tweener', '~> 2.1.1'
To install dependencies run this command:
pod install
Install using Carthage
To integrate install Carthage with brew:
$ brew update
$ brew install carthage
Now, add Tweener to your Cartfile
github "alexrvarela/SwiftTweener" ~> 2.1.1
To install dependencies run this command:
$ carthage update
Finally, drag & drop Tweener.framework to your Xcode Project
Install using Swift Package Manager
To install, add dependencies to your Package.swift
dependencies: [
.package(url: "https://github.com/alexrvarela/SwiftTweener.git", .upToNextMajor(from: "2.1.1"))
]
Install manually
Download, build and copy Tweener.framework to your Xcode project.
Usage
Import Tweener engine to your project:
import Tweener
Animate by default any of these kinds of properties:
Int, Float, Double, CGFloat, CGPoint, CGRect, UIColor, CGAffineTransform, CATransform3D
First set initial state:
myView.alpha = 0.25
myView.frame = CGRect(x:20, y:20, width:50, height:50)
myView.backgroundColor = .blue
Create and add a simple Tween:
Tween(myView)
.duration(1.0)//One second
.ease(.inOutCubic)
.to(
.key(\.alpha, 1.0),
.key(\.frame,CGRect(x:20, y:20, width:250, height:250)),
.key(\.backgroundColor!, .red)
)
.play()
Or use 'from' and 'to' keys:
Tween(myView)
.duration(1.0)//One second
.ease(.inOutCubic)
.from(
.key(\.alpha, 0.25),
.key(\.frame, CGRect(x:20, y:20, width:50, height:50)),
.key(\.backgroundColor!, .blue)
)
.to(
.key(\.alpha, 1.0),
.key(\.frame,CGRect(x:20, y:20, width:250, height:250)),
.key(\.backgroundColor!, .red)
)
.play()
To remove a Tween from Engine simply call stop().
myTween.stop()
Tween chaining
To create and chain a Tween with same target and properties just call .after()
let firstTween = Tween(myViewInstance)
// This creates and chains a new tween whith time delay after 'firstTween'.
let secondTween = firstTween.after()
//This plays firstTween and secondTween.
secondTween.play()
To create and chain a Tween with different target and Type pass the second Tween as parameter.
let firstTween = Tween(myViewInstance)
let secondTween = Tween(otherViewInstance)
// This chains booth and sets the second one after first one.
firstTween.after(secondTween)
//This plays firstTween and secondTween.
secondTween.play()
You can chain as many Tweens as you want.
Tween handlers
Interact with your code using block handlers:
myTween.onStart {
self.backgroundColor = .green
}
myTween.onUpdate {
doAnything()
}
myTween.onComplete {
self.backgroundColor = .red
}
myTween.onOverwrite {
self.backgroundColor = .blue
}
Remove and pause existing Tweens in the Engine
You can pause, resume and remove existing tweens:
For all existing tweens:
Tweener.pauseAllTweens()
Tweener.resumeAllTweens()
Tweener.removeAllTweens()
By target:
Tweener.pauseTweens(target:myView)
Tweener.resumeTweens(target:myView)
Tweener.removeTweens(target:myView)
By specific properties of a target:
Tweener.pauseTweens(target:myView, keys:[\UIView.backgroundColor, \UIView.alpha])
Tweener.resumeTweens(target:myView, keys:[\UIView.backgroundColor, \UIView.alpha])
Tweener.removeTweens(target:myView, keys:[\UIView.backgroundColor, \UIView.alpha])
Unleash your creativity!
Touch point sample:
Drag views sample:
Pause tweens sample:
Easing
This engine is based on Robert Penner's Easing equations
To create a custom easing equation:
extension Ease{
public static let custom = Ease(equation:{ (t, b, c, d) in
//Play with code here!
if t < d/2 {return Ease.inBack.equation(t*2, b, c/2, d)}
return Ease.outElastic.equation((t*2)-d, b+c/2, c/2, d)
})
}
And use it:
Tween(myView)
.ease(.custom)
.to(.key(\.frame, CGRect(x:20.0, y:20.0, width:280.0, height:280.0)))
.play()
Timeline
Add a Tween or animate with Timeline?
It depends on what do you want, a Tween only animates “to” desired value taking the current value of the property as origin, that allows your App to be more dynamic, each Tween is destroyed immediately after completing the animation.
Timeline stores “from” and “to” values of each Tween, contains a collection of reusable Tweens, to create Timeline and add Tweens use this code:
let myTimeline = Timeline()
.add(
myTween1,
myTween2
//etc...
)
.play()
You can interact with Timeline play modes, the default value is Play once, it stops when finished, to change Tmeline play mode:
Loop, repeat forever
myTimeline.playMode(.loop)
Ping Pong, forward and reverse
myTimeline.playMode(.pingPong)
To remove a Timeline from Engine simply call stop().
myTimeline.stop()
Perform parallax scrolling effects controlling your timeline with UIScrollView:
TimelineInspector
You can use the Timeline inspector to debug and edit Tweens
Visualize Tweens in real time:
Edit Tweens:
To create Timeline inspector:
let myInspector = TimelineInspector(timeline:myTimeline)
addSubview(myInspector)
PDFImageView
Cut with the image dependency and easily import your vector assets using PDFImageView, forget to export to SVG and other formats iOs offers native support for PDF with CoreGraphics, this class simply renders one pdf inside a UIImageView
To load your asset named "bee.pdf" from App bundle:
let myAsset = PDFImageView(bundlename:"bee")
addSubview(myAsset)
You can increase or reduce the size of your assets with a simple property:
myAsset.scale = 1.5
Aims
Create more complex and impressive animations using Aims
PathAim
Control motion with paths:
let myPathAim = PathAim(target:myAsset)
myPathAim.path = myBezierPath
To change location at path change this property value:
myPathAim.interpolation = 0.5
And simply animate path interpolation:
Tween(myPathAim)
.from(.key(\.interpolation, 0.0))
.to(.key(\.interpolation, 1.0))
.play()
You can export your paths to code from illustrator with this simple Script:
https://github.com/alexrvarela/generatePathCode
RotationAim
Animate rotation of any view
let myRotationAim = RotationAim(target:myView)
Tween(myRotationAim)
.from(.key(\.angle, 90.0))
.to(.key(\.angle, 360.0))
.play()
ArcAim
Create Arc animations
let myArcAim = ArcAim(target:myView)
//Set desired radius
myArcAim.radius = 100.0
//Animate arc angle
Tween(myArcAim)
.from(.key(\.arcAngle, 0.0))
.to(.key(\.arcAngle, 360.0))
.play()
StringAim
Animate text transitions
//Create string aim
let stringAim = StringAim(target:myUILabel, keyPath:\UILabel.text)
stringAim.from = "hello"
stringAim.to = "hola"
//Set initial interpolation
stringAim.interpolation = 0.0
//Animate, using timeline to repeat forever.
Timeline(
//Create tween with StringAim target and animate interpolation.
Tween(stringAim)
.delay(0.5)
.duration(0.5)
.from(.key(\.interpolation, 0.0))
.to(.key(\.interpolation, 1.0))
.onComplete { self.swapText() }
)
.mode(.loop)
.play()
Play with everything, combine different types of Aim:
TweenVisualizer
Visualize all tweens and timelines in real time
Create a TweenVisualizer and attach it to Tweener's update loop :
let visualizer = TweenVisualizer()
visualizer.center = viewController.view.center
Tweener.addVisualizer(visualizer)
//Add to UIView
addSubview(visualizer)
To detach visualizer from update loop just use this code:
Tweener.removeVisualizer(visualizer)
Also, you can drag, pinch and resize visualizer at your convenience, to resize just drag the bottom-right corner:
This library was created to give dynamism to UI elements, if you are looking to make more complex animations I recommend you implement them with Lottie.
Contributions
Pull requests are welcome!
The next release will include: SwiftUI samples, watchOs & tvOs samples and unit tests.
Authors
- Alejandro Ramírez Varela - Initial work - alexrvarela