2.0.0

This major release of Material Motion focuses includes improvements to the transitioning architecture, the reactive architecture, and new features on many of the interactions.

Breaking changes

• The IndefiniteObservable dependency has been bumped to 4.0.0. View the release notes.

• The Metadata and Inspectable types have been removed from Material Motion. All related APIs have been simplified accordingly.

• The MotionRuntime.toGraphViz API has been removed. There is no replacement API.

Tossable's init(system:draggable:) has been removed. Use init(spring:draggable:) instead.

SelfDismissingTransition's willPresent(fore:dismisser:) is no longer a static method. Implement the method as an instance method instead.

Transition is now a class protocol. This means that only object-types can conform to Transition.

TransitionController's dismisser has been removed. All methods have been moved directly to the TransitionController object.

Tween's keyPositions has been removed. Use offsets instead.

MotionRuntime's interactions(for:filter:) has been removed. Use interactions(ofType:for:) instead.

New features

Reactive architecture

• Subscriptions no longer automatically unsubscribe when the Subscription object is released. Subscriptions will stay active for as long as the head of the stream is alive.

• Reactive types are now global and shared across all instances of MotionRuntime. You can use Reactive(object) to fetch a cached reactive version of a supported type.

• MotionRuntime now supports a .get for UISlider instances. This will return an Observable of the slider's value.

• New operator ignoreUntil.

• New reactive variant of operator rubberBanded(outsideOf:maxLength:).

• New operator for float types toString(format:).

• New ReactiveScrollViewDelegate API turns UIScrollViewDelegate events into observable streams.

For example, the delegate itself is an observable on the scroll view's content offset:

let delegate = ReactiveScrollViewDelegate()
scrollView.delegate = delegate
delegate.x().subscribeToValue { contentOffset in
  print(contentOffset)
}

• New ReactiveButtonTarget API for building reactive UIButtons.

MotionRuntime has a new API start(_:when:is:) for starting interactions when another interaction reaches a given state.

MotionRuntime has a new isBeingManipulated stream. This stream emits true when any Manipulable interaction becomes active and false when all Manipulable interactions come to rest.

Interactions

MotionRuntime now has a new isBeingManipulated property that indicates whether any manipulation interaction is active.

Any interaction that conforms to the new Manipulation type will affect the runtime's isBeingManipulated property.

Draggable now has a resistance property that can be used to create drag resistance beyond a draggable region.

draggable.resistance.perimeter.value = someRect

Tween has new properties for creating repeating animations: repeatCount, repeatDuration, and autoreverses.

These properties directly map to the corresponding properties in Core Animation.

TransitionTween has new initializer values delayBefore and delayAfter.

delayBefore is the delay used when transitioning forward. delayAfter is the delay used when transitioning backward.

• The gesture property for gesture-based interactions is now optional. When nil, the interaction will do nothing.

This is primarily useful when building transitions that have optional interactivity.

Transitions

• New TransitionWithFallback protocol allows transitions to swap themselves out for another transition instance.

• New TransitionWithPresentation protocol allows transitions to customize their presentation using an iOS presentation controller. See the modal dialog case study for an example of using this new functionality.

• New TransitionWithTermination protocol allows transitions to perform cleanup logic at the end of a transition.

TransitionContext's gestureRecognizers is now settable. This makes it possible to add arbitrary gesture recognizers to a transition.

Source changes

API changes

Auto-generated by running:

apidiff origin/stable release-candidate swift MaterialMotion.xcworkspace MaterialMotion

Debugging

Inspectable

removed protocol: Inspectable

Metadata

removed class: Metadata

Interactions

Draggable

new var: resistance in Draggable

modified class: Draggable

Type of change: Declaration
From: public final class Draggable : Gesturable<UIPanGestureRecognizer>, Interaction, Togglable, Stateful
To: public final class Draggable : Gesturable<UIPanGestureRecognizer>, Interaction, Togglable, Manipulation

Manipulation

new protocol: Manipulation

Rotatable

modified class: Rotatable

Type of change: Declaration
From: public final class Rotatable : Gesturable<UIRotationGestureRecognizer>, Interaction, Togglable, Stateful
To: public final class Rotatable : Gesturable<UIRotationGestureRecognizer>, Interaction, Togglable, Manipulation

Scalable

modified class: Scalable

Type of change: Declaration
From: public final class Scalable : Gesturable<UIPinchGestureRecognizer>, Interaction, Togglable, Stateful
To: public final class Scalable : Gesturable<UIPinchGestureRecognizer>, Interaction, Togglable, Manipulation

Tossable

removed method: init(system:draggable:) in Tossable

Tween

new var: offsets in Tween

new var: repeatCount in Tween

new var: repeatDuration in Tween

new var: autoreverses in Tween

removed var: keyPositions in Tween

Reactive architecture

MotionObservableConvertible

new method: ignoreUntil(_:) in MotionObservableConvertible

new method: rubberBanded(outsideOf:maxLength:) in MotionObservableConvertible

new method: toString(format:) in MotionObservableConvertible

MotionRuntime

new method: interactions(ofType:for:) in MotionRuntime

new method: start(_:when:is:) in MotionRuntime

new var: isBeingManipulated in MotionRuntime

removed method: interactions(for:filter:) in MotionRuntime

removed method: asGraphviz() in MotionRuntime

ReactiveButtonTarget

new class: ReactiveButtonTarget

new var: didHighlight in ReactiveButtonTarget

ReactiveUIView

removed class: ReactiveUIView

ReactiveCAShapeLayer

removed class: ReactiveCAShapeLayer

ReactiveScrollViewDelegate

new class: ReactiveScrollViewDelegate

Transitions

SelfDismissingTransition

new method: willPresent(fore:dismisser:) in SelfDismissingTransition

removed static method: willPresent(fore:dismisser:) in SelfDismissingTransition

Transition

removed method: init() in Transition

modified protocol: Transition

Type of change: Declaration
From: public protocol Transition
To: public protocol Transition : class

TransitionWithFallback

new protocol: TransitionWithFallback

new method: fallbackTansition(withContext:) in TransitionWithFallback

TransitionWithPresentation

new protocol: TransitionWithPresentation

new method: presentationController(forPresented:presenting:source:) in TransitionWithPresentation

new method: defaultModalPresentationStyle() in TransitionWithPresentation

TransitionWithTermination

new protocol: TransitionWithTermination

new method: didEndTransition(withContext:runtime:) in TransitionWithTermination

TransitionContext

modified var: gestureRecognizers in TransitionContext

Type of change: Declaration
From: public var gestureRecognizers: Set<UIGestureRecognizer> { get }
To: public let gestureRecognizers: Set<UIGestureRecognizer>

TransitionController

new method: disableSimultaneousRecognition(of:) in TransitionController

new var: presentationController in TransitionController

new method: topEdgeDismisserDelegate(for:) in TransitionController

new var: gestureRecognizers in TransitionController

new var: transition in TransitionController

new var: gestureDelegate in TransitionController

new method: dismissWhenGestureRecognizerBegins(_:) in TransitionController

removed var: transitionType in TransitionController

removed var: dismisser in TransitionController

Non-source changes

1.3.0

Highlights:

Behavioral changes

defaultTransitionSpringTension and defaultTransitionSpringFriction's values have been swapped to match the actual default values for tension and friction. These values were previously incorrectly reversed.

• Operators that do not support Core Animation will no longer throw runtime assertions when receiving Core Animation events. We have an open issue to explore nicer handling of operators and properties that do not support Core Animation.

New features

Runtime

• MotionRuntime now allows you to retrieve interactions associated with a given target with the new interactions(for:filter:) API.

Example usage:

let draggables = runtime.interactions(for: view) { $0 as? Draggable }

Interactions

PathTween, Tween, TransitionTween each have a new convenience initializer that accepts the DispatchTimeInterval enum, making it possible to specify explicit time units. Contributed by Eric Tang.

Example usage:

let tween = Tween<CGFloat>(duration: .milliseconds(300), values: [1, 0])

Draggable, Rotatable, Scalable, and DirectlyManipulable now all conform to the Togglable type, meaning they can be reactively enabled and disabled.

Example usage:

draggable.enabled.value = false // Disables the interaction

Operators

delay(by:) now accepts a DispatchTimeInterval enum, making it possible to specify explicit time units. Contributed by Eric Tang.

let delayedStream = stream.delay(by: .seconds(1))

toString() transforms any stream into a string representation. This is part of our Reactive Controls milestone.

let stringStream = stream.toStream()

visualize(in:) allows you to display a stream's values and changes in your app with a new visualization overlay that appears atop your runtime's container view.

Example usage:

runtime.add(tossable, to: view) { $0.visualize(in: runtime.visualizationView) }

API changes

Runtime

• MotionRuntime's add method now requires that targets conform to AnyObject. This will not affect any of the existing Interactions included with Material Motion. What this change means is that you can no longer build interactions that target non-object types.

Transitions

• TransitionController is now a pure Swift class type. This means TransitionController is no longer visible to Objective-C code. See https://github.com/material-motion/material-motion-swift/issues/108 for our discussion on Objective-C support.

TransitionController now exposes a transitioningDelegate property. TransitionController no longer conforms to UIViewControllerTransitioningDelegate.

// Before
viewController.transitioningDelegate = viewController.transitionController

// After
viewController.transitioningDelegate = viewController.transitionController.transitioningDelegate

Source changes

API changes

Auto-generated by running:

apidiff origin/stable release-candidate swift MaterialMotion.xcworkspace MaterialMotion

MotionRuntime

new method: interactions(for:filter:) in MotionRuntime

modified method: add(_:to:constraints:) in MotionRuntime: targets must now conform to AnyObject.

new var: visualizationView in MotionRuntime

New operators

new method: delay(by:) in MotionObservableConvertible

new method: toString() in MotionObservableConvertible

new method: visualize(_:in:) in MotionObservableConvertible

Interactions

DirectlyManipulable

modified class: DirectlyManipulable

Type of change: Declaration
From: public final class DirectlyManipulable : NSObject, Interaction, Stateful
To: public final class DirectlyManipulable : NSObject, Interaction, Togglable, Stateful

Draggable

modified class: Draggable

Type of change: Declaration
From: public final class Draggable : Gesturable<UIPanGestureRecognizer>, Interaction, Stateful
To: public final class Draggable : Gesturable<UIPanGestureRecognizer>, Interaction, Togglable, Stateful

Gesturable

new var: enabled in Gesturable

PathTween

new method: init(duration:path:system:timeline:) in PathTween

Rotatable

modified class: Rotatable

Type of change: Declaration
From: public final class Rotatable : Gesturable<UIRotationGestureRecognizer>, Interaction, Stateful
To: public final class Rotatable : Gesturable<UIRotationGestureRecognizer>, Interaction, Togglable, Stateful

Scalable

modified class: Scalable

Type of change: Declaration
From: public final class Scalable : Gesturable<UIPinchGestureRecognizer>, Interaction, Stateful
To: public final class Scalable : Gesturable<UIPinchGestureRecognizer>, Interaction, Togglable, Stateful

Tween

new method: init(duration:values:system:timeline:) in Tween

TransitionTween

new method: init(duration:forwardValues:direction:forwardKeyPositions:system:timeline:) in TransitionTween

Transitions

TransitionController

modified class: TransitionController

Type of change: Declaration
From: public final class TransitionController : NSObject
To: public final class TransitionController

new var: transitioningDelegate in TransitionController

ViewControllerDismisser

new var: gestureRecognizers in ViewControllerDismisser

1.2.1

This is a patch release resolving a crashing bug when runtime.shouldVisualizeMotion was enabled and an ArcMove interaction was added to a view without a parent.

Source changes

1.2.0

This minor release introduces a new operator, startWith, which is meant to replace the initialValue operator.

New features

startWith operator

The new startWith operator replaces initialValue and behaves slightly differently: startWith returns a memory stream, which is a stream that stores the last value it received and emits it upon subscription. What this means is that the provided initial value will only be emitted once, ever, and that the resulting stream is guaranteed to emit a value on subscription.

You can use startWith to take a stream that may not initially emit values (like a gesture stream) and prime it with an initial value. For example, we use startWith in the "How to use reactive constraints" example in order to ensure that our axis line property is primed with a value.

let axisCenterX = runtime.get(axisLine.layer).position.x()
runtime.add(Draggable(), to: exampleView) { $0
  .startWith(exampleView.layer.position)
  .xLocked(to: axisCenterX)
}
runtime.add(Draggable(), to: axisLine) { $0.yLocked(to: axisLine.layer.position.y) }

New deprecations

Source changes

API changes

Auto-generated by running:

apidiff origin/stable release-candidate swift MaterialMotion.xcworkspace MaterialMotion

MotionObservableConvertible

new method: startWith(_:) in MotionObservableConvertible

deprecated method: initialValue(_:) in MotionObservableConvertible: Use startWith(_:) instead.

Non-source changes

1.1.0

This is our first minor release. It includes two new interactions and improvements to APIs for the common cases.

Behavioral changes

spring.tension.value = defaultSpringTension
spring.friction.value = defaultSpringFriction
spring.mass.value = defaultSpringMass
spring.suggestedDuration.value = 0

New deprecations

New features

New interactions: ChangeDirection and SlopRegion.

Gesturable interactions can now be initialized with a sequence of gesture recognizers. This makes it easier to create gesturable interactions in transitions where the gesture recognizers are provided as a set.

Spring's system now defaults to Core Animation.

There is a new API for getting a gesture recognizer delegate that's able to coordinate a "drag to dismiss" transition with a vertical scroll view.

let pan = UIPanGestureRecognizer()
pan.delegate = transitionController.dismisser.topEdgeDismisserDelegate(for: scrollView)

Source changes

API changes

Auto-generated by running:

apidiff origin/stable release-candidate swift MaterialMotion.xcworkspace MaterialMotion

New global constants

new global var: defaultTransitionSpringFriction

new global var: defaultTransitionSpringSuggestedDuration

new global var: defaultTransitionSpringTension

new global var: defaultTransitionSpringMass

New interactions

new class: ChangeDirection

new class: SlopRegion

Modified interactions

Gesturable

Affects Draggable, Rotatable, and Scalable.

new method: init(withFirstGestureIn:) in Gesturable

Spring

Type of change: Declaration
From: public class Spring<T> : Interaction, Togglable, Stateful where T : Zeroable
To: public class Spring<T> : Interaction, Togglable, Stateful where T : Subtractable, T : Zeroable

modified method: init(threshold:system:) in Spring

Type of change: Declaration
From: public init(threshold: CGFloat, system: @escaping SpringToStream<T>)
To: public init(threshold: CGFloat = 1, system: @escaping SpringToStream<T> = coreAnimation)

Tossable

modified method: init(spring:draggable:) in Tossable

Type of change: Declaration
From: public init(spring: Spring<CGPoint>, draggable: Draggable = Draggable())
To: public init(spring: Spring<CGPoint> = Spring(), draggable: Draggable = Draggable())

deprecated method: init(system:draggable:) in Tossable. Use init(spring:draggable:) instead.

TransitionSpring

modified class: TransitionSpring

Type of change: Declaration
From: public final class TransitionSpring<T> : Spring<T> where T : Zeroable
To: public final class TransitionSpring<T> : Spring<T> where T : Subtractable, T : Zeroable

modified method: init(back:fore:direction:threshold:system:) in TransitionSpring

Type of change: Declaration
From: public init(back backwardDestination: T, fore forwardDestination: T, direction: ReactiveProperty<TransitionDirection>, threshold: CGFloat, system: @escaping SpringToStream<T>)
To: public init(back backwardDestination: T, fore forwardDestination: T, direction: ReactiveProperty<TransitionDirection>, threshold: CGFloat = default, system: @escaping SpringToStream<T> = default)

Transitions

ViewControllerDismisser

new method: topEdgeDismisserDelegate(for:) in ViewControllerDismisser

Stream changes

new var: onCompletion in CoreAnimationChannelAdd

removed var: onCompletion in CoreAnimationChannelAdd

Non-source changes

1.0.0

Initial stable release of Material Motion. Includes:

Source changes

Non-source changes