Commit 19e673bf by Daniel Dahan

partial rework to MotionTransition and its relating parts

parent 06ec8deb
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
96AEB6A81EE4610F009A3BE0 /* Nodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6831EE4610F009A3BE0 /* Nodes.swift */; }; 96AEB6A81EE4610F009A3BE0 /* Nodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6831EE4610F009A3BE0 /* Nodes.swift */; };
96AEB6A91EE4610F009A3BE0 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6841EE4610F009A3BE0 /* Parser.swift */; }; 96AEB6A91EE4610F009A3BE0 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6841EE4610F009A3BE0 /* Parser.swift */; };
96AEB6AA1EE4610F009A3BE0 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6851EE4610F009A3BE0 /* Regex.swift */; }; 96AEB6AA1EE4610F009A3BE0 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6851EE4610F009A3BE0 /* Regex.swift */; };
96AEB6AB1EE4610F009A3BE0 /* BasePreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6871EE4610F009A3BE0 /* BasePreprocessor.swift */; };
96AEB6AC1EE4610F009A3BE0 /* CascadePreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */; }; 96AEB6AC1EE4610F009A3BE0 /* CascadePreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */; };
96AEB6AD1EE4610F009A3BE0 /* DurationPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */; }; 96AEB6AD1EE4610F009A3BE0 /* DurationPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */; };
96AEB6AE1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */; }; 96AEB6AE1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */; };
...@@ -86,7 +85,6 @@ ...@@ -86,7 +85,6 @@
96AEB6831EE4610F009A3BE0 /* Nodes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Nodes.swift; sourceTree = "<group>"; }; 96AEB6831EE4610F009A3BE0 /* Nodes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Nodes.swift; sourceTree = "<group>"; };
96AEB6841EE4610F009A3BE0 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; }; 96AEB6841EE4610F009A3BE0 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
96AEB6851EE4610F009A3BE0 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = "<group>"; }; 96AEB6851EE4610F009A3BE0 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = "<group>"; };
96AEB6871EE4610F009A3BE0 /* BasePreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasePreprocessor.swift; sourceTree = "<group>"; };
96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CascadePreprocessor.swift; sourceTree = "<group>"; }; 96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CascadePreprocessor.swift; sourceTree = "<group>"; };
96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DurationPreprocessor.swift; sourceTree = "<group>"; }; 96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DurationPreprocessor.swift; sourceTree = "<group>"; };
96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoreSubviewModifiersPreprocessor.swift; sourceTree = "<group>"; }; 96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IgnoreSubviewModifiersPreprocessor.swift; sourceTree = "<group>"; };
...@@ -163,7 +161,6 @@ ...@@ -163,7 +161,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
968989DB1EE65F2B003B8F3D /* MotionPreprocessor.swift */, 968989DB1EE65F2B003B8F3D /* MotionPreprocessor.swift */,
96AEB6871EE4610F009A3BE0 /* BasePreprocessor.swift */,
96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */, 96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */,
96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */, 96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */,
96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */, 96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */,
...@@ -335,7 +332,6 @@ ...@@ -335,7 +332,6 @@
96AEB69E1EE4610F009A3BE0 /* MotionController.swift in Sources */, 96AEB69E1EE4610F009A3BE0 /* MotionController.swift in Sources */,
96AEB6A11EE4610F009A3BE0 /* MotionTransition.swift in Sources */, 96AEB6A11EE4610F009A3BE0 /* MotionTransition.swift in Sources */,
96AEB6971EE4610F009A3BE0 /* Motion+CG.swift in Sources */, 96AEB6971EE4610F009A3BE0 /* Motion+CG.swift in Sources */,
96AEB6AB1EE4610F009A3BE0 /* BasePreprocessor.swift in Sources */,
963150DA1EE51EB4002B0D42 /* MotionAnimationFillMode.swift in Sources */, 963150DA1EE51EB4002B0D42 /* MotionAnimationFillMode.swift in Sources */,
96AEB6AC1EE4610F009A3BE0 /* CascadePreprocessor.swift in Sources */, 96AEB6AC1EE4610F009A3BE0 /* CascadePreprocessor.swift in Sources */,
); );
......
...@@ -94,12 +94,12 @@ extension MotionDebugPlugin:MotionDebugViewDelegate { ...@@ -94,12 +94,12 @@ extension MotionDebugPlugin:MotionDebugViewDelegate {
Motion.shared.update(elapsedTime: Double(seekValue)) Motion.shared.update(elapsedTime: Double(seekValue))
} }
func onPerspectiveChanged(translation: CGPoint, rotation: CGFloat, scale: CGFloat) { func onPerspectiveChanged(translation: CGPoint, rotate: CGFloat, scale: CGFloat) {
var t = CATransform3DIdentity var t = CATransform3DIdentity
t.m34 = -1 / 4000 t.m34 = -1 / 4000
t = CATransform3DTranslate(t, translation.x, translation.y, 0) t = CATransform3DTranslate(t, translation.x, translation.y, 0)
t = CATransform3DScale(t, scale, scale, 1) t = CATransform3DScale(t, scale, scale, 1)
t = CATransform3DRotate(t, rotation, 0, 1, 0) t = CATransform3DRotate(t, rotate, 0, 1, 0)
Motion.shared.container.layer.sublayerTransform = t Motion.shared.container.layer.sublayerTransform = t
} }
...@@ -160,7 +160,7 @@ extension MotionDebugPlugin:MotionDebugViewDelegate { ...@@ -160,7 +160,7 @@ extension MotionDebugPlugin:MotionDebugViewDelegate {
t.m34 = -1 / 4000 t.m34 = -1 / 4000
t = CATransform3DTranslate(t, debugView!.translation.x, debugView!.translation.y, 0) t = CATransform3DTranslate(t, debugView!.translation.x, debugView!.translation.y, 0)
t = CATransform3DScale(t, debugView!.scale, debugView!.scale, 1) t = CATransform3DScale(t, debugView!.scale, debugView!.scale, 1)
t = CATransform3DRotate(t, debugView!.rotation, 0, 1, 0) t = CATransform3DRotate(t, debugView!.rotate, 0, 1, 0)
} else { } else {
for v in Motion.shared.container.subviews { for v in Motion.shared.container.subviews {
animateZPosition(view:v, to:self.zPositionMap[v] ?? 0) animateZPosition(view:v, to:self.zPositionMap[v] ?? 0)
......
...@@ -31,7 +31,7 @@ import UIKit ...@@ -31,7 +31,7 @@ import UIKit
#if os(iOS) #if os(iOS)
protocol MotionDebugViewDelegate: class { protocol MotionDebugViewDelegate: class {
func onProcessSliderChanged(progress: Float) func onProcessSliderChanged(progress: Float)
func onPerspectiveChanged(translation: CGPoint, rotation: CGFloat, scale: CGFloat) func onPerspectiveChanged(translation: CGPoint, rotate: CGFloat, scale: CGFloat)
func on3D(wants3D: Bool) func on3D(wants3D: Bool)
func onDisplayArcCurve(wantsCurve: Bool) func onDisplayArcCurve(wantsCurve: Bool)
func onDone() func onDone()
...@@ -56,7 +56,7 @@ class MotionDebugView: UIView { ...@@ -56,7 +56,7 @@ class MotionDebugView: UIView {
} }
var showOnTop: Bool = false var showOnTop: Bool = false
var rotation: CGFloat = .pi / 6 var rotate: CGFloat = .pi / 6
var scale: CGFloat = 0.6 var scale: CGFloat = 0.6
var translation: CGPoint = .zero var translation: CGPoint = .zero
var progress: Float { var progress: Float {
...@@ -142,15 +142,15 @@ class MotionDebugView: UIView { ...@@ -142,15 +142,15 @@ class MotionDebugView: UIView {
var startRotation: CGFloat = 0 var startRotation: CGFloat = 0
@objc public func pan() { @objc public func pan() {
if panGR.state == .began { if panGR.state == .began {
startRotation = rotation startRotation = rotate
} }
rotation = startRotation + panGR.translation(in: nil).x / 150 rotate = startRotation + panGR.translation(in: nil).x / 150
if rotation > .pi { if rotate > .pi {
rotation -= 2 * .pi rotate -= 2 * .pi
} else if rotation < -.pi { } else if rotate < -.pi {
rotation += 2 * .pi rotate += 2 * .pi
} }
delegate?.onPerspectiveChanged(translation:translation, rotation: rotation, scale:scale) delegate?.onPerspectiveChanged(translation:translation, rotate: rotate, scale:scale)
} }
var startLocation: CGPoint = .zero var startLocation: CGPoint = .zero
...@@ -167,7 +167,7 @@ class MotionDebugView: UIView { ...@@ -167,7 +167,7 @@ class MotionDebugView: UIView {
if pinchGR.numberOfTouches >= 2 { if pinchGR.numberOfTouches >= 2 {
scale = min(1, max(0.2, startScale * pinchGR.scale)) scale = min(1, max(0.2, startScale * pinchGR.scale))
translation = startTranslation + pinchGR.location(in: nil) - startLocation translation = startTranslation + pinchGR.location(in: nil) - startLocation
delegate?.onPerspectiveChanged(translation:translation, rotation: rotation, scale:scale) delegate?.onPerspectiveChanged(translation:translation, rotate: rotate, scale:scale)
} }
default: default:
break break
......
...@@ -209,7 +209,9 @@ extension MotionDefaultAnimationType: MotionStringConvertible { ...@@ -209,7 +209,9 @@ extension MotionDefaultAnimationType: MotionStringConvertible {
} }
} }
class DefaultAnimationPreprocessor: BasePreprocessor { class DefaultAnimationPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
weak var motion: Motion? weak var motion: Motion?
...@@ -232,7 +234,12 @@ class DefaultAnimationPreprocessor: BasePreprocessor { ...@@ -232,7 +234,12 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
return rtn return rtn
} }
override func process(fromViews: [UIView], toViews: [UIView]) { /**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
func process(fromViews: [UIView], toViews: [UIView]) {
guard let motion = motion else { return } guard let motion = motion else { return }
var defaultAnimation = motion.defaultAnimation var defaultAnimation = motion.defaultAnimation
let inNavigationController = motion.isNavigationController let inNavigationController = motion.isNavigationController
...@@ -279,60 +286,60 @@ class DefaultAnimationPreprocessor: BasePreprocessor { ...@@ -279,60 +286,60 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
context[fromView] = [.timingFunction(.standard), .duration(0.35)] context[fromView] = [.timingFunction(.standard), .duration(0.35)]
context[toView] = [.timingFunction(.standard), .duration(0.35)] context[toView] = [.timingFunction(.standard), .duration(0.35)]
let shadowState: [MotionTransition] = [.shadowOpacity(0.5), let shadowState: [MotionTransition] = [.shadow(opacity: 0.5),
.shadowColor(.black), .shadow(color: .black),
.shadowRadius(5), .shadow(radius: 5),
.shadowOffset(.zero), .shadow(offset: .zero),
.masksToBounds(false)] .masksToBounds(false)]
switch defaultAnimation { switch defaultAnimation {
case .push(let direction): case .push(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false) / 3), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false) / 3),
.overlay(color: .black, opacity: 0.1), .overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pull(let direction): case .pull(let direction):
motion.insertToViewFirst = true motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true) / 3), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true) / 3),
.overlay(color: .black, opacity: 0.1)]) .overlay(color: .black, opacity: 0.1)])
case .slide(let direction): case .slide(let direction):
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false))]) context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false))])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true))]) context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true))])
case .zoomSlide(let direction): case .zoomSlide(let direction):
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), .scale(0.8)]) context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), .scale(to: 0.8)])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)), .scale(0.8)]) context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), .scale(to: 0.8)])
case .cover(let direction): case .cover(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1), context[fromView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .uncover(let direction): case .uncover(let direction):
motion.insertToViewFirst = true motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1)]) context[toView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1)])
case .pageIn(let direction): case .pageIn(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)), context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState), .beginWith(transitions: shadowState),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.scale(0.7), context[fromView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1), .overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pageOut(let direction): case .pageOut(let direction):
motion.insertToViewFirst = true motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadowOpacity(0), .shadow(opacity: 0),
.beginWith(modifiers: shadowState)]) .beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.scale(0.7), context[toView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1)]) .overlay(color: .black, opacity: 0.1)])
case .fade: case .fade:
// TODO: clean up this. overFullScreen logic shouldn't be here // TODO: clean up this. overFullScreen logic shouldn't be here
...@@ -348,15 +355,15 @@ class DefaultAnimationPreprocessor: BasePreprocessor { ...@@ -348,15 +355,15 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
} }
#endif #endif
context[toView]!.append(.durationMatchLongest) context[toView]!.append(.preferredDurationMatchesLongest)
context[fromView]!.append(.durationMatchLongest) context[fromView]!.append(.preferredDurationMatchesLongest)
case .zoom: case .zoom:
motion.insertToViewFirst = true motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.scale(1.3), .fade]) context[fromView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[toView]!.append(contentsOf: [.scale(0.7)]) context[toView]!.append(contentsOf: [.scale(to: 0.7)])
case .zoomOut: case .zoomOut:
context[toView]!.append(contentsOf: [.scale(1.3), .fade]) context[toView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[fromView]!.append(contentsOf: [.scale(0.7)]) context[fromView]!.append(contentsOf: [.scale(to: 0.7)])
default: default:
fatalError("Not implemented") fatalError("Not implemented")
} }
......
...@@ -232,30 +232,30 @@ extension CALayer { ...@@ -232,30 +232,30 @@ extension CALayer {
case let .transform(transform): case let .transform(transform):
a.append(MotionBasicAnimation.transform(transform: transform)) a.append(MotionBasicAnimation.transform(transform: transform))
case let .rotationAngle(angle): case let .rotate(angle):
let rotate = MotionBasicAnimation.rotation(angle: angle) let rotate = MotionBasicAnimation.rotate(angle: angle)
a.append(rotate) a.append(rotate)
case let .rotationAngleX(angle): case let .rotateX(angle):
a.append(MotionBasicAnimation.rotationX(angle: angle)) a.append(MotionBasicAnimation.rotateX(angle: angle))
case let .rotationAngleY(angle): case let .rotateY(angle):
a.append(MotionBasicAnimation.rotationY(angle: angle)) a.append(MotionBasicAnimation.rotateY(angle: angle))
case let .rotationAngleZ(angle): case let .rotateZ(angle):
a.append(MotionBasicAnimation.rotationZ(angle: angle)) a.append(MotionBasicAnimation.rotateZ(angle: angle))
case let .spin(rotations): case let .spin(rotates):
a.append(MotionBasicAnimation.spin(rotations: rotations)) a.append(MotionBasicAnimation.spin(rotates: rotates))
case let .spinX(rotations): case let .spinX(rotates):
a.append(MotionBasicAnimation.spinX(rotations: rotations)) a.append(MotionBasicAnimation.spinX(rotates: rotates))
case let .spinY(rotations): case let .spinY(rotates):
a.append(MotionBasicAnimation.spinY(rotations: rotations)) a.append(MotionBasicAnimation.spinY(rotates: rotates))
case let .spinZ(rotations): case let .spinZ(rotates):
a.append(MotionBasicAnimation.spinZ(rotations: rotations)) a.append(MotionBasicAnimation.spinZ(rotates: rotates))
case let .scale(to): case let .scale(to):
a.append(MotionBasicAnimation.scale(to: to)) a.append(MotionBasicAnimation.scale(to: to))
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
import UIKit import UIKit
fileprivate let parameterRegex = "(?:\\-?\\d+(\\.?\\d+)?)|\\w+" fileprivate let parameterRegex = "(?:\\-?\\d+(\\.?\\d+)?)|\\w+"
fileprivate let modifiersRegex = "(\\w+)(?:\\(([^\\)]*)\\))?" fileprivate let transitionsRegex = "(\\w+)(?:\\(([^\\)]*)\\))?"
internal extension NSObject { internal extension NSObject {
func copyWithArchiver() -> Any? { func copyWithArchiver() -> Any? {
......
...@@ -170,7 +170,7 @@ public extension UIView { ...@@ -170,7 +170,7 @@ public extension UIView {
} }
extension UIView { extension UIView {
/// Computes the rotation of the view. /// Computes the rotate of the view.
open var motionRotationAngle: CGFloat { open var motionRotationAngle: CGFloat {
get { get {
return CGFloat(atan2f(Float(transform.b), Float(transform.a))) * 180 / CGFloat(Double.pi) return CGFloat(atan2f(Float(transform.b), Float(transform.a))) * 180 / CGFloat(Double.pi)
......
...@@ -41,10 +41,10 @@ public enum MotionAnimation { ...@@ -41,10 +41,10 @@ public enum MotionAnimation {
case borderWidth(CGFloat) case borderWidth(CGFloat)
case cornerRadius(CGFloat) case cornerRadius(CGFloat)
case transform(CATransform3D) case transform(CATransform3D)
case rotationAngle(CGFloat) case rotate(CGFloat)
case rotationAngleX(CGFloat) case rotateX(CGFloat)
case rotationAngleY(CGFloat) case rotateY(CGFloat)
case rotationAngleZ(CGFloat) case rotateZ(CGFloat)
case spin(CGFloat) case spin(CGFloat)
case spinX(CGFloat) case spinX(CGFloat)
case spinY(CGFloat) case spinY(CGFloat)
...@@ -81,10 +81,10 @@ public enum MotionAnimationKeyPath: String { ...@@ -81,10 +81,10 @@ public enum MotionAnimationKeyPath: String {
case borderWidth case borderWidth
case cornerRadius case cornerRadius
case transform case transform
case rotation = "transform.rotation" case rotate = "transform.rotate"
case rotationX = "transform.rotation.x" case rotateX = "transform.rotate.x"
case rotationY = "transform.rotation.y" case rotateY = "transform.rotate.y"
case rotationZ = "transform.rotation.z" case rotateZ = "transform.rotate.z"
case scale = "transform.scale" case scale = "transform.scale"
case scaleX = "transform.scale.x" case scaleX = "transform.scale.x"
case scaleY = "transform.scale.y" case scaleY = "transform.scale.y"
...@@ -184,90 +184,90 @@ public struct MotionBasicAnimation { ...@@ -184,90 +184,90 @@ public struct MotionBasicAnimation {
} }
/** /**
Creates a CABasicAnimation for the transform.rotation key path. Creates a CABasicAnimation for the transform.rotate key path.
- Parameter angle: An optional CGFloat. - Parameter angle: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func rotation(angle: CGFloat) -> CABasicAnimation { public static func rotate(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation) let animation = CABasicAnimation(keyPath: .rotate)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.x key path. Creates a CABasicAnimation for the transform.rotate.x key path.
- Parameter angle: An optional CGFloat. - Parameter angle: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func rotationX(angle: CGFloat) -> CABasicAnimation { public static func rotateX(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX) let animation = CABasicAnimation(keyPath: .rotateX)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.y key path. Creates a CABasicAnimation for the transform.rotate.y key path.
- Parameter angle: An optional CGFloat. - Parameter angle: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func rotationY(angle: CGFloat) -> CABasicAnimation { public static func rotateY(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY) let animation = CABasicAnimation(keyPath: .rotateY)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.z key path. Creates a CABasicAnimation for the transform.rotate.z key path.
- Parameter angle: An optional CGFloat. - Parameter angle: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func rotationZ(angle: CGFloat) -> CABasicAnimation { public static func rotateZ(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ) let animation = CABasicAnimation(keyPath: .rotateZ)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation key path. Creates a CABasicAnimation for the transform.rotate key path.
- Parameter rotations: An optional CGFloat. - Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func spin(rotations: CGFloat) -> CABasicAnimation { public static func spin(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation) let animation = CABasicAnimation(keyPath: .rotate)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.x key path. Creates a CABasicAnimation for the transform.rotate.x key path.
- Parameter rotations: An optional CGFloat. - Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func spinX(rotations: CGFloat) -> CABasicAnimation { public static func spinX(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX) let animation = CABasicAnimation(keyPath: .rotateX)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.y key path. Creates a CABasicAnimation for the transform.rotate.y key path.
- Parameter rotations: An optional CGFloat. - Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func spinY(rotations: CGFloat) -> CABasicAnimation { public static func spinY(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY) let animation = CABasicAnimation(keyPath: .rotateY)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation return animation
} }
/** /**
Creates a CABasicAnimation for the transform.rotation.z key path. Creates a CABasicAnimation for the transform.rotate.z key path.
- Parameter rotations: An optional CGFloat. - Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func spinZ(rotations: CGFloat) -> CABasicAnimation { public static func spinZ(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ) let animation = CABasicAnimation(keyPath: .rotateZ)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations)) animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation return animation
} }
......
...@@ -47,18 +47,18 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { ...@@ -47,18 +47,18 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/** /**
Called before any animation. Called before any animation.
Override this method when you want to preprocess modifiers for views Override this method when you want to preprocess transitions for views
- Parameters: - Parameters:
- context: object holding all parsed and changed modifiers, - context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController - fromViews: A flattened list of all views from source ViewController
- toViews: A flattened list of all views from destination ViewController - toViews: A flattened list of all views from destination ViewController
To check a view's modifiers: To check a view's transitions:
context[view] context[view]
context[view, "modifierName"] context[view, "modifierName"]
To set a view's modifiers: To set a view's transitions:
context[view] = [("modifier1", ["parameter1"]), ("modifier2", [])] context[view] = [("modifier1", ["parameter1"]), ("modifier2", [])]
context[view, "modifier1"] = ["parameter1", "parameter2"] context[view, "modifier1"] = ["parameter1", "parameter2"]
...@@ -69,7 +69,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { ...@@ -69,7 +69,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/** /**
- Returns: return true if the plugin can handle animating the view. - Returns: return true if the plugin can handle animating the view.
- Parameters: - Parameters:
- context: object holding all parsed and changed modifiers, - context: object holding all parsed and changed transitions,
- view: the view to check whether or not the plugin can handle the animation - view: the view to check whether or not the plugin can handle the animation
- isAppearing: true if the view is isAppearing(i.e. a view in destination ViewController) - isAppearing: true if the view is isAppearing(i.e. a view in destination ViewController)
If return true, Motion won't animate and won't let any other plugins animate this view. If return true, Motion won't animate and won't let any other plugins animate this view.
...@@ -82,7 +82,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { ...@@ -82,7 +82,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
Note: views in `fromViews` & `toViews` are hidden already. Unhide then if you need to take snapshots. Note: views in `fromViews` & `toViews` are hidden already. Unhide then if you need to take snapshots.
- Parameters: - Parameters:
- context: object holding all parsed and changed modifiers, - context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController (filtered by `canAnimate`) - fromViews: A flattened list of all views from source ViewController (filtered by `canAnimate`)
- toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`) - toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`)
- Returns: The duration needed to complete the animation - Returns: The duration needed to complete the animation
...@@ -121,7 +121,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { ...@@ -121,7 +121,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/** /**
For supporting interactive animation only. For supporting interactive animation only.
This method is called when user wants to override animation modifiers during an interactive animation This method is called when user wants to override animation transitions during an interactive animation
- Parameters: - Parameters:
- state: the target state to override - state: the target state to override
......
...@@ -93,7 +93,7 @@ public struct MotionTargetState { ...@@ -93,7 +93,7 @@ public struct MotionTargetState {
public var timingFunction: CAMediaTimingFunction? public var timingFunction: CAMediaTimingFunction?
public var arc: CGFloat? public var arc: CGFloat?
public var source: String? public var motionIdentifier: String?
public var cascade: (TimeInterval, CascadeDirection, Bool)? public var cascade: (TimeInterval, CascadeDirection, Bool)?
public var ignoreSubviewModifiers: Bool? public var ignoreSubviewModifiers: Bool?
......
...@@ -38,14 +38,14 @@ extension MotionTransition: MotionStringConvertible { ...@@ -38,14 +38,14 @@ extension MotionTransition: MotionStringConvertible {
case "fade": case "fade":
return .fade return .fade
case "opacity": case "opacity":
return MotionTransition.opacity(parameters.getFloat(0) ?? 1) return MotionTransition.fade(to: parameters.getFloat(0) ?? 1)
case "position": case "position":
return .position(CGPoint(x: parameters.getCGFloat(0) ?? 0, y: parameters.getCGFloat(1) ?? 0)) return .position(to: CGPoint(x: parameters.getCGFloat(0) ?? 0, y: parameters.getCGFloat(1) ?? 0))
case "size": case "size":
return .size(CGSize(width: parameters.getCGFloat(0) ?? 0, height: parameters.getCGFloat(1) ?? 0)) return .size(CGSize(width: parameters.getCGFloat(0) ?? 0, height: parameters.getCGFloat(1) ?? 0))
case "scale": case "scale":
if parameters.count == 1 { if parameters.count == 1 {
return .scale(parameters.getCGFloat(0) ?? 1) return .scale(to: parameters.getCGFloat(0) ?? 1)
} else { } else {
return .scale(x: parameters.getCGFloat(0) ?? 1, return .scale(x: parameters.getCGFloat(0) ?? 1,
y: parameters.getCGFloat(1) ?? 1, y: parameters.getCGFloat(1) ?? 1,
...@@ -73,8 +73,8 @@ extension MotionTransition: MotionStringConvertible { ...@@ -73,8 +73,8 @@ extension MotionTransition: MotionStringConvertible {
if let duration = parameters.getDouble(0) { if let duration = parameters.getDouble(0) {
return .duration(duration) return .duration(duration)
} }
case "durationMatchLongest": case "preferredDurationMatchesLongest":
return .durationMatchLongest return .preferredDurationMatchesLongest
case "delay": case "delay":
if let delay = parameters.getDouble(0) { if let delay = parameters.getDouble(0) {
return .delay(delay) return .delay(delay)
...@@ -100,10 +100,10 @@ extension MotionTransition: MotionStringConvertible { ...@@ -100,10 +100,10 @@ extension MotionTransition: MotionStringConvertible {
let direction = CascadeDirection(directionString) { let direction = CascadeDirection(directionString) {
cascadeDirection = direction cascadeDirection = direction
} }
return .cascade(delta: parameters.getDouble(0) ?? 0.02, direction: cascadeDirection, delayMatchedViews:parameters.getBool(2) ?? false) return .cascade(delta: parameters.getDouble(0) ?? 0.02, direction: cascadeDirection, animationDelayUntilMatchedViews:parameters.getBool(2) ?? false)
case "source": case "source":
if let motionIdentifier = parameters.get(0)?.name { if let motionIdentifier = parameters.get(0)?.name {
return .source(motionIdentifier: motionIdentifier) return .motionIdentifier(motionIdentifier)
} }
case "useGlobalCoordinateSpace": case "useGlobalCoordinateSpace":
return .useGlobalCoordinateSpace return .useGlobalCoordinateSpace
......
...@@ -29,482 +29,453 @@ ...@@ -29,482 +29,453 @@
import UIKit import UIKit
public final class MotionTransition { public final class MotionTransition {
internal let apply:(inout MotionTargetState) -> Void /// A reference to the callback that applies the MotionTargetState.
public init(applyFunction:@escaping (inout MotionTargetState) -> Void) { internal let apply: (inout MotionTargetState) -> Void
apply = applyFunction
}
}
// basic modifiers
extension MotionTransition {
/** /**
Fade the view during transition An initializer that accepts a given callback.
- Parameter applyFunction: A given callback.
*/ */
public static var fade = MotionTransition { targetState in public init(applyFunction: @escaping (inout MotionTargetState) -> Void) {
targetState.opacity = 0 apply = applyFunction
} }
}
extension MotionTransition {
/** /**
Set the opacity for the view to animate from/to. Animates the view with a matching motion identifier.
- Parameters: - Parameter _ identifier: A String.
- opacity: opacity for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func opacity(_ opacity: Float) -> MotionTransition { public static func motionIdentifier(_ identifier: String) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.opacity = opacity $0.motionIdentifier = identifier
}
} }
/**
Force don't fade view during transition
*/
public static var forceNonFade = MotionTransition { targetState in
targetState.nonFade = true
} }
/** /**
Set the position for the view to animate from/to. Animates the view's current masksToBounds to the
- Parameters: given masksToBounds.
- position: position for the view to animate from/to - Parameter masksToBounds: A boolean value indicating the
masksToBounds state.
- Returns: A MotionTransition.
*/ */
public static func position(_ position: CGPoint) -> MotionTransition { public static func masksToBounds(_ masksToBounds: Bool) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.position = position $0.masksToBounds = masksToBounds
} }
} }
/** /**
Set the size for the view to animate from/to. Animates the view's current background color to the
- Parameters: given color.
- size: size for the view to animate from/to - Parameter color: A UIColor.
- Returns: A MotionTransition.
*/ */
public static func size(_ size: CGSize) -> MotionTransition { public static func background(color: UIColor) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.size = size $0.backgroundColor = color.cgColor
} }
} }
}
// transform modifiers
extension MotionTransition {
/** /**
Set the transform for the view to animate from/to. Will override previous perspective, scale, translate, & rotate modifiers Animates the view's current border color to the
- Parameters: given color.
- t: the CATransform3D object - Parameter color: A UIColor.
- Returns: A MotionTransition.
*/ */
public static func transform(_ t: CATransform3D) -> MotionTransition { public static func border(color: UIColor) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.transform = t $0.borderColor = color.cgColor
} }
} }
/** /**
Set the perspective on the transform. use in combination with the rotate modifier. Animates the view's current border width to the
- Parameters: given width.
- perspective: set the camera distance of the transform - Parameter width: A CGFloat.
- Returns: A MotionTransition.
*/ */
public static func perspective(_ perspective: CGFloat) -> MotionTransition { public static func border(width: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
var transform = targetState.transform ?? CATransform3DIdentity $0.borderWidth = width
transform.m34 = 1.0 / -perspective
targetState.transform = transform
} }
} }
/** /**
Scale 3d Animates the view's current corner radius to the
- Parameters: given radius.
- x: scale factor on x axis, default 1 - Parameter radius: A CGFloat.
- y: scale factor on y axis, default 1 - Returns: A MotionTransition.
- z: scale factor on z axis, default 1
*/ */
public static func scale(x: CGFloat = 1, y: CGFloat = 1, z: CGFloat = 1) -> MotionTransition { public static func corner(radius: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.transform = CATransform3DScale(targetState.transform ?? CATransform3DIdentity, x, y, z) $0.cornerRadius = radius
} }
} }
/** /**
Scale in x & y axis Animates the view's current transform (perspective, scale, rotate)
- Parameters: to the given one.
- xy: scale factor in both x & y axis - Parameter _ transform: A CATransform3D.
- Returns: A MotionTransition.
*/ */
public static func scale(_ xy: CGFloat) -> MotionTransition { public static func transform(_ transform: CATransform3D) -> MotionTransition {
return .scale(x: xy, y: xy) return MotionTransition {
$0.transform = transform
}
} }
/** /**
Translate 3d Animates the view's current perspective to the gievn one through
- Parameters: a CATransform3D object.
- x: translation distance on x axis in display pixel, default 0 - Parameter _ perspective: A CGFloat.
- y: translation distance on y axis in display pixel, default 0 - Returns: A MotionTransition.
- z: translation distance on z axis in display pixel, default 0
*/ */
public static func translate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition { public static func perspective(_ perspective: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.transform = CATransform3DTranslate(targetState.transform ?? CATransform3DIdentity, x, y, z) var t = $0.transform ?? CATransform3DIdentity
} t.m34 = 1 / -perspective
$0.transform = t
} }
public static func translate(_ point: CGPoint, z: CGFloat = 0) -> MotionTransition {
return translate(x: point.x, y: point.y, z: z)
} }
/** /**
Rotate 3d Animates the view's current rotate to the given x, y,
- Parameters: and z values.
- x: rotation on x axis in radian, default 0 - Parameter x: A CGFloat.
- y: rotation on y axis in radian, default 0 - Parameter y: A CGFloat.
- z: rotation on z axis in radian, default 0 - Parameter z: A CGFloat.
- Returns: A MotionTransition.
*/ */
public static func rotate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition { public static func rotate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.transform = CATransform3DRotate(targetState.transform ?? CATransform3DIdentity, x, 1, 0, 0) var t = $0.transform ?? CATransform3DIdentity
targetState.transform = CATransform3DRotate(targetState.transform!, y, 0, 1, 0) t = CATransform3DRotate(t, x, 1, 0, 0)
targetState.transform = CATransform3DRotate(targetState.transform!, z, 0, 0, 1) t = CATransform3DRotate(t, y, 0, 1, 0)
$0.transform = CATransform3DRotate(t, z, 0, 0, 1)
} }
} }
/**
Animates the view's current rotate to the given point.
- Parameter _ point: A CGPoint.
- Parameter z: A CGFloat, default is 0.
- Returns: A MotionTransition.
*/
public static func rotate(_ point: CGPoint, z: CGFloat = 0) -> MotionTransition { public static func rotate(_ point: CGPoint, z: CGFloat = 0) -> MotionTransition {
return rotate(x: point.x, y: point.y, z: z) return .rotate(x: point.x, y: point.y, z: z)
} }
/** /**
Rotate 2d Rotate 2d.
- Parameters: - Parameter _ z: A CGFloat.
- z: rotation in radian - Returns: A MotionTransition.
*/ */
public static func rotate(_ z: CGFloat) -> MotionTransition { public static func rotate(_ z: CGFloat) -> MotionTransition {
return .rotate(z: z) return .rotate(z: z)
} }
}
extension MotionTransition {
/** /**
Set the opacity for the view to animate from/to. Animates the view's current scale to the given x, y, z scale values.
- Parameters: - Parameter x: A CGFloat.
- opacity: opacity for the view to animate from/to - Parameter y: A CGFloat.
- Parameter z: A CGFloat.
- Returns: A MotionTransition.
*/ */
public static func opacity(_ opacity: CGFloat) -> MotionTransition { public static func scale(x: CGFloat = 1, y: CGFloat = 1, z: CGFloat = 1) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.opacity = Float(opacity) $0.transform = CATransform3DScale($0.transform ?? CATransform3DIdentity, x, y, z)
} }
} }
/** /**
Set the backgroundColor for the view to animate from/to. Animates the view's current x & y scale to the given scale value.
- Parameters: - Parameter to scale: A CGFloat.
- backgroundColor: backgroundColor for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func backgroundColor(_ backgroundColor: UIColor) -> MotionTransition { public static func scale(to scale: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return .scale(x: scale, y: scale)
targetState.backgroundColor = backgroundColor.cgColor
}
} }
/** /**
Set the cornerRadius for the view to animate from/to. Animates the view's current translation to the given
- Parameters: x, y, and z values.
- cornerRadius: cornerRadius for the view to animate from/to - Parameter x: A CGFloat.
- Parameter y: A CGFloat.
- Parameter z: A CGFloat.
- Returns: A MotionTransition.
*/ */
public static func cornerRadius(_ cornerRadius: CGFloat) -> MotionTransition { public static func translate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.cornerRadius = cornerRadius $0.transform = CATransform3DTranslate($0.transform ?? CATransform3DIdentity, x, y, z)
} }
} }
/** /**
Set the zPosition for the view to animate from/to. Animates the view's current translation to the given
- Parameters: point value (x & y), and a z value.
- zPosition: zPosition for the view to animate from/to - Parameter to point: A CGPoint.
- Parameter z: A CGFloat, default is 0.
- Returns: A MotionTransition.
*/ */
public static func zPosition(_ zPosition: CGFloat) -> MotionTransition { public static func translate(to point: CGPoint, z: CGFloat = 0) -> MotionTransition {
return MotionTransition { targetState in return .translate(x: point.x, y: point.y, z: z)
targetState.zPosition = zPosition
}
} }
/** /**
Set the contentsRect for the view to animate from/to. Animates the view's current position to the given point.
- Parameters: - Parameter to point: A CGPoint.
- contentsRect: contentsRect for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func contentsRect(_ contentsRect: CGRect) -> MotionTransition { public static func position(to point: CGPoint) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.contentsRect = contentsRect $0.position = point
} }
} }
/// Forces the view to not fade during a transition.
public static var forceNonFade = MotionTransition { targetState in
targetState.nonFade = true
}
/// Fades the view out during a transition.
public static var fade = MotionTransition {
$0.opacity = 0
}
/** /**
Set the contentsScale for the view to animate from/to. Animates the view's current opacity to the given one.
- Parameters: - Parameter to opacity: A Float value.
- contentsScale: contentsScale for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func contentsScale(_ contentsScale: CGFloat) -> MotionTransition { public static func fade(to opacity: Float) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.contentsScale = contentsScale $0.opacity = opacity
} }
} }
/** /**
Set the borderWidth for the view to animate from/to. Animates the view's current zPosition to the given position.
- Parameters: - Parameter _ position: An Int.
- borderWidth: borderWidth for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func borderWidth(_ borderWidth: CGFloat) -> MotionTransition { public static func zPosition(_ position: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.borderWidth = borderWidth $0.zPosition = position
} }
} }
/** /**
Set the borderColor for the view to animate from/to. Animates the view's current size to the given one.
- Parameters: - Parameter _ size: A CGSize.
- borderColor: borderColor for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func borderColor(_ borderColor: UIColor) -> MotionTransition { public static func size(_ size: CGSize) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.borderColor = borderColor.cgColor $0.size = size
} }
} }
/** /**
Set the shadowColor for the view to animate from/to. Animates the view's current shadow path to the given one.
- Parameters: - Parameter path: A CGPath.
- shadowColor: shadowColor for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func shadowColor(_ shadowColor: UIColor) -> MotionTransition { public static func shadow(path: CGPath) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.shadowColor = shadowColor.cgColor $0.shadowPath = path
} }
} }
/** /**
Set the shadowOpacity for the view to animate from/to. Animates the view's current shadow color to the given one.
- Parameters: - Parameter color: A UIColor.
- shadowOpacity: shadowOpacity for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func shadowOpacity(_ shadowOpacity: CGFloat) -> MotionTransition { public static func shadow(color: UIColor) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.shadowOpacity = Float(shadowOpacity) $0.shadowColor = color.cgColor
} }
} }
/** /**
Set the shadowOffset for the view to animate from/to. Animates the view's current shadow offset to the given one.
- Parameters: - Parameter offset: A CGSize.
- shadowOffset: shadowOffset for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func shadowOffset(_ shadowOffset: CGSize) -> MotionTransition { public static func shadow(offset: CGSize) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.shadowOffset = shadowOffset $0.shadowOffset = offset
} }
} }
/** /**
Set the shadowRadius for the view to animate from/to. Animates the view's current shadow opacity to the given one.
- Parameters: - Parameter opacity: A CGFloat.
- shadowRadius: shadowRadius for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func shadowRadius(_ shadowRadius: CGFloat) -> MotionTransition { public static func shadow(opacity: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.shadowRadius = shadowRadius $0.shadowOpacity = Float(opacity)
} }
} }
/** /**
Set the shadowPath for the view to animate from/to. Animates the view's current shadow radius to the given one.
- Parameters: - Parameter radius: A CGFloat.
- shadowPath: shadowPath for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func shadowPath(_ shadowPath: CGPath) -> MotionTransition { public static func shadow(radius: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.shadowPath = shadowPath $0.shadowRadius = radius
} }
} }
/** /**
Set the masksToBounds for the view to animate from/to. Animates the view's contents rect to the given one.
- Parameters: - Parameter rect: A CGRect.
- masksToBounds: masksToBounds for the view to animate from/to - Returns: A MotionTransition.
*/ */
public static func masksToBounds(_ masksToBounds: Bool) -> MotionTransition { public static func contents(rect: CGRect) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.masksToBounds = masksToBounds $0.contentsRect = rect
} }
} }
/** /**
Create an overlay on the animating view. Animates the view's contents scale to the given one.
- Parameters: - Parameter scale: A CGFloat.
- color: color of the overlay - Returns: A MotionTransition.
- opacity: opacity of the overlay
*/ */
public static func overlay(color: UIColor, opacity: CGFloat) -> MotionTransition { public static func contents(scale: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.overlay = (color.cgColor, opacity) $0.contentsScale = scale
} }
} }
}
// timing modifiers
extension MotionTransition {
/** /**
Sets the duration of the animation for a given view. If not used, Motion will use determine the duration based on the distance and size changes. The duration of the view's animation.
- Parameters: - Parameter _ duration: A TimeInterval.
- duration: duration of the animation - Returns: A MotionTransition.
Note: a duration of .infinity means matching the duration of the longest animation. same as .durationMatchLongest
*/ */
public static func duration(_ duration: TimeInterval) -> MotionTransition { public static func duration(_ duration: TimeInterval) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.duration = duration $0.duration = duration
} }
} }
/** /**
Sets the duration of the animation for a given view to match the longest animation of the transition. Sets the view's animation duration to the longest
running animation within a transition.
*/ */
public static var durationMatchLongest: MotionTransition = MotionTransition { targetState in public static var preferredDurationMatchesLongest = MotionTransition.duration(.infinity)
targetState.duration = .infinity
}
/** /**
Sets the delay of the animation for a given view. Delays the animation of a given view.
- Parameters: - Parameter _ time: TimeInterval.
- delay: delay of the animation - Returns: A MotionTransition.
*/ */
public static func delay(_ delay: TimeInterval) -> MotionTransition { public static func delay(_ time: TimeInterval) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.delay = delay $0.delay = time
} }
} }
/** /**
Sets the timing function of the animation for a given view. If not used, Motion will use determine the timing function based on whether or not the view is entering or exiting the screen. Sets the view's timing function for the animation.
- Parameters: - Parameter _ timingFunction: A CAMediaTimingFunction.
- timingFunction: timing function of the animation - Returns: A MotionTransition.
*/ */
public static func timingFunction(_ timingFunction: CAMediaTimingFunction) -> MotionTransition { public static func timingFunction(_ timingFunction: CAMediaTimingFunction) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.timingFunction = timingFunction $0.timingFunction = timingFunction
} }
} }
/** /**
(iOS 9+) Use spring animation with custom stiffness & damping. The duration will be automatically calculated. Will be ignored if arc, timingFunction, or duration is set. Available in iOS 9+, animates a view using the spring API,
- Parameters: given a stiffness and damping.
- stiffness: stiffness of the spring - Parameter stiffness: A CGFlloat.
- damping: damping of the spring - Parameter damping: A CGFloat.
- Returns: A MotionTransition.
*/ */
@available(iOS 9, *) @available(iOS 9, *)
public static func spring(stiffness: CGFloat, damping: CGFloat) -> MotionTransition { public static func spring(stiffness: CGFloat, damping: CGFloat) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.spring = (stiffness, damping) $0.spring = (stiffness, damping)
} }
} }
}
// other modifiers
extension MotionTransition {
/** /**
Transition from/to the state of the view with matching motionIdentifier Animates the natural curve of a view. A value of 1 represents
Will also force the view to use global coordinate space. a curve in a downward direction, and a value of -1
represents a curve in an upward direction.
The following layer properties will be animated from the given view. - Parameter intensity: A CGFloat.
- Returns: A MotionTransition.
position
bounds.size
cornerRadius
transform
shadowColor
shadowOpacity
shadowOffset
shadowRadius
shadowPath
Note that the following properties **won't** be taken from the source view.
backgroundColor
borderWidth
borderColor
- Parameters:
- motionIdentifier: the source view's motionId.
*/ */
public static func source(motionIdentifier: String) -> MotionTransition { public static func arc(intensity: CGFloat = 1) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.source = motionIdentifier $0.arc = intensity
} }
} }
/** /**
Works in combination with position modifier to apply a natural curve when moving to the destination. Animates subviews with an increasing delay between each animation.
*/ - Parameter delta: A TimeInterval.
public static var arc: MotionTransition = .arc() - Parameter direction: A CascadeDirection.
- Parameter animationDelayUntilMatchedViews: A boolean indicating whether
/** or not to delay the subview animation until all have started.
Works in combination with position modifier to apply a natural curve when moving to the destination. - Returns: A MotionTransition.
- Parameters:
- intensity: a value of 1 represent a downward natural curve ╰. a value of -1 represent a upward curve ╮.
default is 1.
*/ */
public static func arc(intensity: CGFloat = 1) -> MotionTransition { public static func cascade(delta: TimeInterval = 0.02, direction: CascadeDirection = .topToBottom, animationDelayUntilMatchedViews: Bool = false) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition {
targetState.arc = intensity $0.cascade = (delta, direction, animationDelayUntilMatchedViews)
} }
} }
/** /**
Cascade applys increasing delay modifiers to subviews Creates an overlay on the animating view with a given color and opacity.
- Parameter color: A UIColor.
- Parameter opacity: A CGFloat.
- Returns: A MotionTransition.
*/ */
public static var cascade: MotionTransition = .cascade() public static func overlay(color: UIColor, opacity: CGFloat) -> MotionTransition {
return MotionTransition {
/** $0.overlay = (color.cgColor, opacity)
Cascade applys increasing delay modifiers to subviews
- Parameters:
- delta: delay in between each animation
- direction: cascade direction
- delayMatchedViews: whether or not to delay matched subviews until all cascading animation have started
*/
public static func cascade(delta: TimeInterval = 0.02,
direction: CascadeDirection = .topToBottom,
delayMatchedViews: Bool = false) -> MotionTransition {
return MotionTransition { targetState in
targetState.cascade = (delta, direction, delayMatchedViews)
} }
} }
} }
// advance modifiers // advance transitions
extension MotionTransition { extension MotionTransition {
/** /**
Apply modifiers directly to the view at the start of the transition. Apply transitions directly to the view at the start of the transition.
The modifiers supplied here won't be animated. The transitions supplied here won't be animated.
For source views, modifiers are set directly at the begining of the animation. For source views, transitions are set directly at the begining of the animation.
For destination views, they replace the target state (final appearance). For destination views, they replace the target state (final appearance).
*/ */
public static func beginWith(modifiers: [MotionTransition]) -> MotionTransition { public static func beginWith(transitions: [MotionTransition]) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition { targetState in
if targetState.beginState == nil { if targetState.beginState == nil {
targetState.beginState = MotionTargetState.MotionTargetStateWrapper(state: []) targetState.beginState = MotionTargetState.MotionTargetStateWrapper(state: [])
} }
targetState.beginState!.state.append(contentsOf: modifiers) targetState.beginState!.state.append(contentsOf: transitions)
} }
} }
/** /**
Apply modifiers directly to the view at the start of the transition if the view is matched with another view. Apply transitions directly to the view at the start of the transition if the view is matched with another view.
The modifiers supplied here won't be animated. The transitions supplied here won't be animated.
For source views, modifiers are set directly at the begining of the animation. For source views, transitions are set directly at the begining of the animation.
For destination views, they replace the target state (final appearance). For destination views, they replace the target state (final appearance).
*/ */
public static func beginWithIfMatched(modifiers: [MotionTransition]) -> MotionTransition { public static func beginWithIfMatched(transitions: [MotionTransition]) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition { targetState in
if targetState.beginStateIfMatched == nil { if targetState.beginStateIfMatched == nil {
targetState.beginStateIfMatched = [] targetState.beginStateIfMatched = []
} }
targetState.beginStateIfMatched!.append(contentsOf: modifiers) targetState.beginStateIfMatched!.append(contentsOf: transitions)
} }
} }
...@@ -538,7 +509,7 @@ extension MotionTransition { ...@@ -538,7 +509,7 @@ extension MotionTransition {
/** /**
ignore all motionTransitions attributes for a view's subviews. ignore all motionTransitions attributes for a view's subviews.
- Parameters: - Parameters:
- recursive: if false, will only ignore direct subviews' modifiers. default false. - recursive: if false, will only ignore direct subviews' transitions. default false.
*/ */
public static func ignoreSubviewModifiers(recursive: Bool = false) -> MotionTransition { public static func ignoreSubviewModifiers(recursive: Bool = false) -> MotionTransition {
return MotionTransition { targetState in return MotionTransition { targetState in
......
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import UIKit
class BasePreprocessor: MotionPreprocessor {
weak public var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {}
}
...@@ -70,8 +70,11 @@ public enum CascadeDirection { ...@@ -70,8 +70,11 @@ public enum CascadeDirection {
} }
} }
class CascadePreprocessor: BasePreprocessor { class CascadePreprocessor: MotionPreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) { /// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
process(views:fromViews) process(views:fromViews)
process(views:toViews) process(views:toViews)
} }
......
...@@ -8,9 +8,16 @@ ...@@ -8,9 +8,16 @@
import UIKit import UIKit
class DurationPreprocessor: BasePreprocessor { class DurationPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
override func process(fromViews: [UIView], toViews: [UIView]) { /**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
func process(fromViews: [UIView], toViews: [UIView]) {
var maxDuration: TimeInterval = 0 var maxDuration: TimeInterval = 0
maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews) maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews)) maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews))
......
...@@ -28,8 +28,11 @@ ...@@ -28,8 +28,11 @@
import UIKit import UIKit
class IgnoreSubviewModifiersPreprocessor: BasePreprocessor { class IgnoreSubviewModifiersPreprocessor: MotionPreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) { /// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
process(views:fromViews) process(views:fromViews)
process(views:toViews) process(views:toViews)
} }
......
...@@ -28,8 +28,16 @@ ...@@ -28,8 +28,16 @@
import UIKit import UIKit
class MatchPreprocessor: BasePreprocessor { class MatchPreprocessor: MotionPreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) { /// A reference to a MotionContext.
weak var context: MotionContext!
/**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews { for tv in toViews {
guard let id = tv.motionIdentifier, let fv = context.sourceView(for: id) else { continue } guard let id = tv.motionIdentifier, let fv = context.sourceView(for: id) else { continue }
...@@ -37,15 +45,15 @@ class MatchPreprocessor: BasePreprocessor { ...@@ -37,15 +45,15 @@ class MatchPreprocessor: BasePreprocessor {
var fvState = context[fv] ?? MotionTargetState() var fvState = context[fv] ?? MotionTargetState()
if let beginStateIfMatched = tvState.beginStateIfMatched { if let beginStateIfMatched = tvState.beginStateIfMatched {
tvState.append(.beginWith(modifiers: beginStateIfMatched)) tvState.append(.beginWith(transitions: beginStateIfMatched))
} }
if let beginStateIfMatched = fvState.beginStateIfMatched { if let beginStateIfMatched = fvState.beginStateIfMatched {
fvState.append(.beginWith(modifiers: beginStateIfMatched)) fvState.append(.beginWith(transitions: beginStateIfMatched))
} }
// match is just a two-way source effect // match is just a two-way source effect
tvState.source = id tvState.motionIdentifier = id
fvState.source = id fvState.motionIdentifier = id
fvState.arc = tvState.arc fvState.arc = tvState.arc
fvState.duration = tvState.duration fvState.duration = tvState.duration
......
...@@ -28,15 +28,18 @@ ...@@ -28,15 +28,18 @@
import UIKit import UIKit
class SourcePreprocessor: BasePreprocessor { class SourcePreprocessor: MotionPreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) { /// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
for fv in fromViews { for fv in fromViews {
guard let id = context[fv]?.source, guard let id = context[fv]?.motionIdentifier,
let tv = context.destinationView(for: id) else { continue } let tv = context.destinationView(for: id) else { continue }
prepareFor(view: fv, targetView: tv) prepareFor(view: fv, targetView: tv)
} }
for tv in toViews { for tv in toViews {
guard let id = context[tv]?.source, guard let id = context[tv]?.motionIdentifier,
let fv = context.sourceView(for: id) else { continue } let fv = context.sourceView(for: id) else { continue }
prepareFor(view: tv, targetView: fv) prepareFor(view: tv, targetView: fv)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment