Home

Awesome

MotionMachine logo

swift platforms license

MotionMachine provides a modular, powerful, and generic platform for manipulating values, whether that be animating UI elements or interpolating property values in your own classes. It offers sensible default functionality that abstracts most of the hard work away, allowing you to focus on your work. While it is type-agnostic, MotionMachine does support most major UIKit types out of the box and provides syntactic sugar to easily manipulate them. But it's also easy to dive in and modify for your own needs, whether that be custom motion classes, supporting custom value types, or new easing equations.

Getting Started

Get started with the Motion Classes guide for detailed explanations and examples.

Also check out the Examples project to see all the MotionMachine classes in action, or dive deep into the source Documentation.

Introduction

MotionGroup animation

This complex animation was created with the code sample below. These Motion classes animate the NSLayoutConstraints of the circle views (the constraints object in the target parameter is a dictionary of NSLayoutConstraint references) as well as one of their backgroundColor properties. A MotionGroup object is used to synchronize the four Motion objects and reverse their movements.

let group = MotionGroup()

.add(Motion(target: constraints["circleX"]!,
        properties: [PropertyData("constant", 200.0)],
          duration: 1.0,
            easing: EasingQuartic.easeInOut()))

.add(Motion(target: constraints["circleY"]!,
        properties: [PropertyData("constant", 250.0)],
          duration: 1.4,
            easing: EasingElastic.easeInOut()))

.add(Motion(target: circle,
        properties: [PropertyData("backgroundColor.blue", 0.9)],
          duration: 1.2,
            easing: EasingQuartic.easeInOut()))

.add(Motion(target: constraints["circle2X"]!,
        properties: [PropertyData("constant", 300.0)],
          duration: 1.2,
            easing: EasingQuadratic.easeInOut())
            .reverses(withEasing: EasingQuartic.easeInOut()))

.start()

How does this work?

All of the included motion classes in MotionMachine adopt the Moveable protocol, which enables them to work seamlessly together. By using the MotionGroup and MotionSequence collection classes to control multiple motion objects – even nesting multiple layers – you can create complex animations with little effort.

Motion

Motion uses a KVC keyPath (i.e. "frame.origin.x") to target specific properties of an object and transform their values over a period of time via an easing equation. In this example, we pass in PropertyStates structs to the statesForProperties convenience initializer to provide ending value states for the transform and backgroundColor properties of the target object.


let new_color = UIColor.init(red: 91.0/255.0, green:189.0/255.0, blue:231.0/255.0, alpha:1.0)
let circle = circles[0]

motion = Motion(target: circle,
   statesForProperties: [
    PropertyStates(path: "transform", end: circle.transform.scaledBy(x: 1.5, y: 1.5)),
    PropertyStates(path: "backgroundColor", end: new_color)
    ],
              duration: 2.0,
                easing: EasingBack.easeInOut(overshoot: 0.5))
.reverses()
.start()

Motion animation

MotionGroup

MotionGroup is a MoveableCollection class that manages a group of Moveable objects, controlling their movements in parallel. It's handy for controlling and synchronizing multiple Moveable objects. MotionGroup can even control other MoveableCollection objects. In the below example, we told the MotionGroup to reverse and synchronize its child motions while doing so. What this means is that it will pause all motions after the forward movement is done, and only then will it reverse them. In this case, the horizontal movements pause while waiting for the Motion which modifies the second circle's backgroundColor to finish its 3 second duration.


// the MotionGroup will wait for all child motions to finish moving forward before starting their reverse motions
group = MotionGroup().reverses(syncsChildMotions: true)

// move first circle horizontally
let horizontal1 = Motion(target: constraints["x1"]!,
                         properties: [PropertyData("constant", 250.0)],
                         duration: 1.5,
                         easing: EasingSine.easeOut())
.reverses()
group.add(horizontal1)

// reverse and repeat horizontal movement of second circle once, with a subtle overshoot easing
let horizontal2 = Motion(target: constraints["x2"]!,
                  properties: [PropertyData("constant", 250.0)],
                  duration: 1.0,
                  easing: EasingBack.easeOut(overshoot: 0.12))
.reverses()
group.add(horizontal2)

// change backgroundColor of second circle
let color = Motion(target: circles[1],
                   statesForProperties: [PropertyStates(path: "backgroundColor", end: UIColor.init(red: 91.0/255.0, green:189.0/255.0, blue:231.0/255.0, alpha:1.0))],
                   duration: 3.0,
                   easing: EasingQuadratic.easeInOut())
group.add(color)

.start()

MotionGroup animation

MotionSequence

MotionSequence is a MoveableCollection class which moves a collection of Moveable objects in sequential order, even other MoveableCollection objects. MotionSequence provides a powerful and easy way of chaining together value transformations of object properties to do keyframing or to create complex and fluid compound animations of many objects.


// create a reversing MotionSequence with its reversingMode set to contiguous to create a fluid animation from its child motions
sequence = MotionSequence().reverses(.contiguous)

// set up motions for each circle and add them to the MotionSequence
for x in 0..<4 {
    // motion to animate a topAnchor constraint down
    let down = Motion(target: constraints["y\(x)"]!,
                      properties: [PropertyData("constant", 60.0)],
                      duration: 0.4,
                      easing: EasingQuartic.easeInOut())

    // motion to change background color of circle
    let color = Motion(target: circles[x],
                       statesForProperties: [PropertyStates(path: "backgroundColor", end: UIColor.init(red: 91.0/255.0, green:189.0/255.0, blue:231.0/255.0, alpha:1.0))],
                       duration: 0.3,
                       easing: EasingQuadratic.easeInOut())

    // wrap the Motions in a MotionGroup and set it to reverse
    let group = MotionGroup(motions: [down, color]).reverses(syncsChildMotions: true)

    // add group to the MotionSequence
    sequence.add(group)
}
sequence.start()

MotionSequence animation

Installation

You can add MotionMachine to an Xcode project by adding it as a Swift package dependency.

.product(name: "MotionMachine", package: "MotionMachine")

Compatibility

MotionMachine currently requires:

Caveats

Credits

MotionMachine was created by Brett Walker. It is loosely based on the author's Objective-C library PMTween.

License

MotionMachine is licensed under the MIT License. See LICENSE for details.

I'd love to know if you use MotionMachine in your projects!