Commit 19e673bf by Daniel Dahan

partial rework to MotionTransition and its relating parts

parent 06ec8deb
......@@ -42,7 +42,6 @@
96AEB6A81EE4610F009A3BE0 /* Nodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6831EE4610F009A3BE0 /* Nodes.swift */; };
96AEB6A91EE4610F009A3BE0 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6841EE4610F009A3BE0 /* Parser.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 */; };
96AEB6AD1EE4610F009A3BE0 /* DurationPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */; };
96AEB6AE1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */; };
......@@ -86,7 +85,6 @@
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>"; };
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>"; };
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>"; };
......@@ -163,7 +161,6 @@
isa = PBXGroup;
children = (
968989DB1EE65F2B003B8F3D /* MotionPreprocessor.swift */,
96AEB6871EE4610F009A3BE0 /* BasePreprocessor.swift */,
96AEB6881EE4610F009A3BE0 /* CascadePreprocessor.swift */,
96AEB6891EE4610F009A3BE0 /* DurationPreprocessor.swift */,
96AEB68A1EE4610F009A3BE0 /* IgnoreSubviewModifiersPreprocessor.swift */,
......@@ -335,7 +332,6 @@
96AEB69E1EE4610F009A3BE0 /* MotionController.swift in Sources */,
96AEB6A11EE4610F009A3BE0 /* MotionTransition.swift in Sources */,
96AEB6971EE4610F009A3BE0 /* Motion+CG.swift in Sources */,
96AEB6AB1EE4610F009A3BE0 /* BasePreprocessor.swift in Sources */,
963150DA1EE51EB4002B0D42 /* MotionAnimationFillMode.swift in Sources */,
96AEB6AC1EE4610F009A3BE0 /* CascadePreprocessor.swift in Sources */,
);
......
......@@ -94,12 +94,12 @@ extension MotionDebugPlugin:MotionDebugViewDelegate {
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
t.m34 = -1 / 4000
t = CATransform3DTranslate(t, translation.x, translation.y, 0)
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
}
......@@ -160,7 +160,7 @@ extension MotionDebugPlugin:MotionDebugViewDelegate {
t.m34 = -1 / 4000
t = CATransform3DTranslate(t, debugView!.translation.x, debugView!.translation.y, 0)
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 {
for v in Motion.shared.container.subviews {
animateZPosition(view:v, to:self.zPositionMap[v] ?? 0)
......
......@@ -31,7 +31,7 @@ import UIKit
#if os(iOS)
protocol MotionDebugViewDelegate: class {
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 onDisplayArcCurve(wantsCurve: Bool)
func onDone()
......@@ -56,7 +56,7 @@ class MotionDebugView: UIView {
}
var showOnTop: Bool = false
var rotation: CGFloat = .pi / 6
var rotate: CGFloat = .pi / 6
var scale: CGFloat = 0.6
var translation: CGPoint = .zero
var progress: Float {
......@@ -142,15 +142,15 @@ class MotionDebugView: UIView {
var startRotation: CGFloat = 0
@objc public func pan() {
if panGR.state == .began {
startRotation = rotation
startRotation = rotate
}
rotation = startRotation + panGR.translation(in: nil).x / 150
if rotation > .pi {
rotation -= 2 * .pi
} else if rotation < -.pi {
rotation += 2 * .pi
rotate = startRotation + panGR.translation(in: nil).x / 150
if rotate > .pi {
rotate -= 2 * .pi
} else if rotate < -.pi {
rotate += 2 * .pi
}
delegate?.onPerspectiveChanged(translation:translation, rotation: rotation, scale:scale)
delegate?.onPerspectiveChanged(translation:translation, rotate: rotate, scale:scale)
}
var startLocation: CGPoint = .zero
......@@ -167,7 +167,7 @@ class MotionDebugView: UIView {
if pinchGR.numberOfTouches >= 2 {
scale = min(1, max(0.2, startScale * pinchGR.scale))
translation = startTranslation + pinchGR.location(in: nil) - startLocation
delegate?.onPerspectiveChanged(translation:translation, rotation: rotation, scale:scale)
delegate?.onPerspectiveChanged(translation:translation, rotate: rotate, scale:scale)
}
default:
break
......
......@@ -209,8 +209,10 @@ extension MotionDefaultAnimationType: MotionStringConvertible {
}
}
class DefaultAnimationPreprocessor: BasePreprocessor {
class DefaultAnimationPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
weak var motion: Motion?
init(motion: Motion) {
......@@ -232,7 +234,12 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
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 }
var defaultAnimation = motion.defaultAnimation
let inNavigationController = motion.isNavigationController
......@@ -279,60 +286,60 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
context[fromView] = [.timingFunction(.standard), .duration(0.35)]
context[toView] = [.timingFunction(.standard), .duration(0.35)]
let shadowState: [MotionTransition] = [.shadowOpacity(0.5),
.shadowColor(.black),
.shadowRadius(5),
.shadowOffset(.zero),
let shadowState: [MotionTransition] = [.shadow(opacity: 0.5),
.shadow(color: .black),
.shadow(radius: 5),
.shadow(offset: .zero),
.masksToBounds(false)]
switch defaultAnimation {
case .push(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState),
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState),
.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),
.timingFunction(.deceleration)])
case .pull(let direction):
motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState)])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true) / 3),
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true) / 3),
.overlay(color: .black, opacity: 0.1)])
case .slide(let direction):
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false))])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true))])
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false))])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true))])
case .zoomSlide(let direction):
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), .scale(0.8)])
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)), .scale(0.8)])
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)), .scale(to: 0.8)])
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)), .scale(to: 0.8)])
case .cover(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState),
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState),
.timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)])
case .uncover(let direction):
motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState)])
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.overlay(color: .black, opacity: 0.1)])
case .pageIn(let direction):
context[toView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: true)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState),
context[toView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: true)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState),
.timingFunction(.deceleration)])
context[fromView]!.append(contentsOf: [.scale(0.7),
context[fromView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1),
.timingFunction(.deceleration)])
case .pageOut(let direction):
motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadowOpacity(0),
.beginWith(modifiers: shadowState)])
context[toView]!.append(contentsOf: [.scale(0.7),
context[fromView]!.append(contentsOf: [.translate(to: shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
.beginWith(transitions: shadowState)])
context[toView]!.append(contentsOf: [.scale(to: 0.7),
.overlay(color: .black, opacity: 0.1)])
case .fade:
// TODO: clean up this. overFullScreen logic shouldn't be here
......@@ -348,15 +355,15 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
}
#endif
context[toView]!.append(.durationMatchLongest)
context[fromView]!.append(.durationMatchLongest)
context[toView]!.append(.preferredDurationMatchesLongest)
context[fromView]!.append(.preferredDurationMatchesLongest)
case .zoom:
motion.insertToViewFirst = true
context[fromView]!.append(contentsOf: [.scale(1.3), .fade])
context[toView]!.append(contentsOf: [.scale(0.7)])
context[fromView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[toView]!.append(contentsOf: [.scale(to: 0.7)])
case .zoomOut:
context[toView]!.append(contentsOf: [.scale(1.3), .fade])
context[fromView]!.append(contentsOf: [.scale(0.7)])
context[toView]!.append(contentsOf: [.scale(to: 1.3), .fade])
context[fromView]!.append(contentsOf: [.scale(to: 0.7)])
default:
fatalError("Not implemented")
}
......
......@@ -232,30 +232,30 @@ extension CALayer {
case let .transform(transform):
a.append(MotionBasicAnimation.transform(transform: transform))
case let .rotationAngle(angle):
let rotate = MotionBasicAnimation.rotation(angle: angle)
case let .rotate(angle):
let rotate = MotionBasicAnimation.rotate(angle: angle)
a.append(rotate)
case let .rotationAngleX(angle):
a.append(MotionBasicAnimation.rotationX(angle: angle))
case let .rotateX(angle):
a.append(MotionBasicAnimation.rotateX(angle: angle))
case let .rotationAngleY(angle):
a.append(MotionBasicAnimation.rotationY(angle: angle))
case let .rotateY(angle):
a.append(MotionBasicAnimation.rotateY(angle: angle))
case let .rotationAngleZ(angle):
a.append(MotionBasicAnimation.rotationZ(angle: angle))
case let .rotateZ(angle):
a.append(MotionBasicAnimation.rotateZ(angle: angle))
case let .spin(rotations):
a.append(MotionBasicAnimation.spin(rotations: rotations))
case let .spin(rotates):
a.append(MotionBasicAnimation.spin(rotates: rotates))
case let .spinX(rotations):
a.append(MotionBasicAnimation.spinX(rotations: rotations))
case let .spinX(rotates):
a.append(MotionBasicAnimation.spinX(rotates: rotates))
case let .spinY(rotations):
a.append(MotionBasicAnimation.spinY(rotations: rotations))
case let .spinY(rotates):
a.append(MotionBasicAnimation.spinY(rotates: rotates))
case let .spinZ(rotations):
a.append(MotionBasicAnimation.spinZ(rotations: rotations))
case let .spinZ(rotates):
a.append(MotionBasicAnimation.spinZ(rotates: rotates))
case let .scale(to):
a.append(MotionBasicAnimation.scale(to: to))
......
......@@ -29,7 +29,7 @@
import UIKit
fileprivate let parameterRegex = "(?:\\-?\\d+(\\.?\\d+)?)|\\w+"
fileprivate let modifiersRegex = "(\\w+)(?:\\(([^\\)]*)\\))?"
fileprivate let transitionsRegex = "(\\w+)(?:\\(([^\\)]*)\\))?"
internal extension NSObject {
func copyWithArchiver() -> Any? {
......
......@@ -170,7 +170,7 @@ public extension UIView {
}
extension UIView {
/// Computes the rotation of the view.
/// Computes the rotate of the view.
open var motionRotationAngle: CGFloat {
get {
return CGFloat(atan2f(Float(transform.b), Float(transform.a))) * 180 / CGFloat(Double.pi)
......
......@@ -41,10 +41,10 @@ public enum MotionAnimation {
case borderWidth(CGFloat)
case cornerRadius(CGFloat)
case transform(CATransform3D)
case rotationAngle(CGFloat)
case rotationAngleX(CGFloat)
case rotationAngleY(CGFloat)
case rotationAngleZ(CGFloat)
case rotate(CGFloat)
case rotateX(CGFloat)
case rotateY(CGFloat)
case rotateZ(CGFloat)
case spin(CGFloat)
case spinX(CGFloat)
case spinY(CGFloat)
......@@ -81,10 +81,10 @@ public enum MotionAnimationKeyPath: String {
case borderWidth
case cornerRadius
case transform
case rotation = "transform.rotation"
case rotationX = "transform.rotation.x"
case rotationY = "transform.rotation.y"
case rotationZ = "transform.rotation.z"
case rotate = "transform.rotate"
case rotateX = "transform.rotate.x"
case rotateY = "transform.rotate.y"
case rotateZ = "transform.rotate.z"
case scale = "transform.scale"
case scaleX = "transform.scale.x"
case scaleY = "transform.scale.y"
......@@ -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.
- Returns: A CABasicAnimation.
*/
public static func rotation(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation)
public static func rotate(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotate)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
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.
- Returns: A CABasicAnimation.
*/
public static func rotationX(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX)
public static func rotateX(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateX)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
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.
- Returns: A CABasicAnimation.
*/
public static func rotationY(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY)
public static func rotateY(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateY)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
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.
- Returns: A CABasicAnimation.
*/
public static func rotationZ(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ)
public static func rotateZ(angle: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateZ)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * angle / 180))
return animation
}
/**
Creates a CABasicAnimation for the transform.rotation key path.
- Parameter rotations: An optional CGFloat.
Creates a CABasicAnimation for the transform.rotate key path.
- Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation.
*/
public static func spin(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotation)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
public static func spin(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotate)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation
}
/**
Creates a CABasicAnimation for the transform.rotation.x key path.
- Parameter rotations: An optional CGFloat.
Creates a CABasicAnimation for the transform.rotate.x key path.
- Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation.
*/
public static func spinX(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationX)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
public static func spinX(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateX)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation
}
/**
Creates a CABasicAnimation for the transform.rotation.y key path.
- Parameter rotations: An optional CGFloat.
Creates a CABasicAnimation for the transform.rotate.y key path.
- Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation.
*/
public static func spinY(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationY)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
public static func spinY(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateY)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation
}
/**
Creates a CABasicAnimation for the transform.rotation.z key path.
- Parameter rotations: An optional CGFloat.
Creates a CABasicAnimation for the transform.rotate.z key path.
- Parameter rotates: An optional CGFloat.
- Returns: A CABasicAnimation.
*/
public static func spinZ(rotations: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotationZ)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotations))
public static func spinZ(rotates: CGFloat) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .rotateZ)
animation.toValue = NSNumber(value: Double(CGFloat(Double.pi) * 2 * rotates))
return animation
}
......
......@@ -47,18 +47,18 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/**
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:
- 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
- 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, "modifierName"]
To set a view's modifiers:
To set a view's transitions:
context[view] = [("modifier1", ["parameter1"]), ("modifier2", [])]
context[view, "modifier1"] = ["parameter1", "parameter2"]
......@@ -69,7 +69,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/**
- Returns: return true if the plugin can handle animating the view.
- 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
- 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.
......@@ -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.
- 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`)
- toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`)
- Returns: The duration needed to complete the animation
......@@ -121,7 +121,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
/**
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:
- state: the target state to override
......
......@@ -93,7 +93,7 @@ public struct MotionTargetState {
public var timingFunction: CAMediaTimingFunction?
public var arc: CGFloat?
public var source: String?
public var motionIdentifier: String?
public var cascade: (TimeInterval, CascadeDirection, Bool)?
public var ignoreSubviewModifiers: Bool?
......
......@@ -38,14 +38,14 @@ extension MotionTransition: MotionStringConvertible {
case "fade":
return .fade
case "opacity":
return MotionTransition.opacity(parameters.getFloat(0) ?? 1)
return MotionTransition.fade(to: parameters.getFloat(0) ?? 1)
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":
return .size(CGSize(width: parameters.getCGFloat(0) ?? 0, height: parameters.getCGFloat(1) ?? 0))
case "scale":
if parameters.count == 1 {
return .scale(parameters.getCGFloat(0) ?? 1)
return .scale(to: parameters.getCGFloat(0) ?? 1)
} else {
return .scale(x: parameters.getCGFloat(0) ?? 1,
y: parameters.getCGFloat(1) ?? 1,
......@@ -73,8 +73,8 @@ extension MotionTransition: MotionStringConvertible {
if let duration = parameters.getDouble(0) {
return .duration(duration)
}
case "durationMatchLongest":
return .durationMatchLongest
case "preferredDurationMatchesLongest":
return .preferredDurationMatchesLongest
case "delay":
if let delay = parameters.getDouble(0) {
return .delay(delay)
......@@ -100,10 +100,10 @@ extension MotionTransition: MotionStringConvertible {
let direction = CascadeDirection(directionString) {
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":
if let motionIdentifier = parameters.get(0)?.name {
return .source(motionIdentifier: motionIdentifier)
return .motionIdentifier(motionIdentifier)
}
case "useGlobalCoordinateSpace":
return .useGlobalCoordinateSpace
......
/*
* 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 {
}
}
class CascadePreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
class CascadePreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
process(views:fromViews)
process(views:toViews)
}
......
......@@ -8,15 +8,22 @@
import UIKit
class DurationPreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
var maxDuration: TimeInterval = 0
maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews))
setDurationForInfiniteDuration(views: fromViews, duration: maxDuration)
setDurationForInfiniteDuration(views: toViews, duration: maxDuration)
}
class DurationPreprocessor: MotionPreprocessor {
/// 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]) {
var maxDuration: TimeInterval = 0
maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews))
setDurationForInfiniteDuration(views: fromViews, duration: maxDuration)
setDurationForInfiniteDuration(views: toViews, duration: maxDuration)
}
func optimizedDurationFor(view: UIView) -> TimeInterval {
let targetState = context[view]!
......
......@@ -28,8 +28,11 @@
import UIKit
class IgnoreSubviewModifiersPreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
class IgnoreSubviewModifiersPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
process(views:fromViews)
process(views:toViews)
}
......
......@@ -28,49 +28,57 @@
import UIKit
class MatchPreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews {
guard let id = tv.motionIdentifier, let fv = context.sourceView(for: id) else { continue }
var tvState = context[tv] ?? MotionTargetState()
var fvState = context[fv] ?? MotionTargetState()
if let beginStateIfMatched = tvState.beginStateIfMatched {
tvState.append(.beginWith(modifiers: beginStateIfMatched))
}
if let beginStateIfMatched = fvState.beginStateIfMatched {
fvState.append(.beginWith(modifiers: beginStateIfMatched))
}
// match is just a two-way source effect
tvState.source = id
fvState.source = id
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
fvState.delay = tvState.delay
fvState.spring = tvState.spring
tvState.opacity = 0
let forceNonFade = tvState.nonFade || fvState.nonFade
let isNonOpaque = !fv.isOpaque || fv.alpha < 1 || !tv.isOpaque || tv.alpha < 1
if !forceNonFade && isNonOpaque {
// cross fade if from/toViews are not opaque
fvState.opacity = 0
} else {
// no cross fade in this case, fromView is always displayed during the transition.
fvState.opacity = nil
// we dont want two shadows showing up. Therefore we disable toView's shadow when fromView is able to display its shadow
if !fv.layer.masksToBounds && fvState.displayShadow {
tvState.displayShadow = false
class MatchPreprocessor: MotionPreprocessor {
/// 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 {
guard let id = tv.motionIdentifier, let fv = context.sourceView(for: id) else { continue }
var tvState = context[tv] ?? MotionTargetState()
var fvState = context[fv] ?? MotionTargetState()
if let beginStateIfMatched = tvState.beginStateIfMatched {
tvState.append(.beginWith(transitions: beginStateIfMatched))
}
if let beginStateIfMatched = fvState.beginStateIfMatched {
fvState.append(.beginWith(transitions: beginStateIfMatched))
}
// match is just a two-way source effect
tvState.motionIdentifier = id
fvState.motionIdentifier = id
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
fvState.delay = tvState.delay
fvState.spring = tvState.spring
tvState.opacity = 0
let forceNonFade = tvState.nonFade || fvState.nonFade
let isNonOpaque = !fv.isOpaque || fv.alpha < 1 || !tv.isOpaque || tv.alpha < 1
if !forceNonFade && isNonOpaque {
// cross fade if from/toViews are not opaque
fvState.opacity = 0
} else {
// no cross fade in this case, fromView is always displayed during the transition.
fvState.opacity = nil
// we dont want two shadows showing up. Therefore we disable toView's shadow when fromView is able to display its shadow
if !fv.layer.masksToBounds && fvState.displayShadow {
tvState.displayShadow = false
}
}
context[tv] = tvState
context[fv] = fvState
}
}
context[tv] = tvState
context[fv] = fvState
}
}
}
......@@ -28,15 +28,18 @@
import UIKit
class SourcePreprocessor: BasePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
class SourcePreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
func process(fromViews: [UIView], toViews: [UIView]) {
for fv in fromViews {
guard let id = context[fv]?.source,
guard let id = context[fv]?.motionIdentifier,
let tv = context.destinationView(for: id) else { continue }
prepareFor(view: fv, targetView: tv)
}
for tv in toViews {
guard let id = context[tv]?.source,
guard let id = context[tv]?.motionIdentifier,
let fv = context.sourceView(for: id) else { continue }
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