Commit 65aef2c1 by Daniel Dahan

adding animation sequence of logic to transitions

parent 45cd5276
......@@ -13,7 +13,7 @@
964C153D1EDCF6EA00F0869D /* Motion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964C153C1EDCF6EA00F0869D /* Motion.swift */; };
964C15471EDD001A00F0869D /* MotionTransitionAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */; };
9657A6AC1EDA1601004461DE /* MotionObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6AB1EDA1601004461DE /* MotionObserver.swift */; };
9657A6AE1EDA19D8004461DE /* MotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6AD1EDA19D8004461DE /* MotionAnimator.swift */; };
9657A6AE1EDA19D8004461DE /* MotionTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6AD1EDA19D8004461DE /* MotionTransitionAnimator.swift */; };
9657A6B31EDA63FC004461DE /* MotionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6B21EDA63FC004461DE /* MotionContext.swift */; };
966C539E1EDD207800A82A57 /* Motion+UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C539D1EDD207800A82A57 /* Motion+UIView.swift */; };
966C53A01EDD20DA00A82A57 /* Motion+UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C539F1EDD20DA00A82A57 /* Motion+UIViewController.swift */; };
......@@ -23,7 +23,7 @@
966C53B51EDD327D00A82A57 /* MotionCascadeDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */; };
966C53B71EDD328F00A82A57 /* MotionCoordinateSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */; };
966C53B91EDD366800A82A57 /* MotionTransitionPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */; };
966C53BB1EDD381E00A82A57 /* MotionCaptureSubviewPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BA1EDD381E00A82A57 /* MotionCaptureSubviewPreprocessor.swift */; };
966C53BB1EDD381E00A82A57 /* MotionSubviewPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BA1EDD381E00A82A57 /* MotionSubviewPreprocessor.swift */; };
966C53BD1EDD396800A82A57 /* MotionAnimationFillMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BC1EDD396800A82A57 /* MotionAnimationFillMode.swift */; };
966C53BF1EDD399400A82A57 /* MotionAnimationTimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BE1EDD399400A82A57 /* MotionAnimationTimingFunction.swift */; };
96BFC1701E63C3460075DE1F /* MotionController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96C98DE31E4382B100B22906 /* MotionController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -37,7 +37,7 @@
964C153C1EDCF6EA00F0869D /* Motion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Motion.swift; sourceTree = "<group>"; };
964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionAnimation.swift; sourceTree = "<group>"; };
9657A6AB1EDA1601004461DE /* MotionObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionObserver.swift; sourceTree = "<group>"; };
9657A6AD1EDA19D8004461DE /* MotionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimator.swift; sourceTree = "<group>"; };
9657A6AD1EDA19D8004461DE /* MotionTransitionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionAnimator.swift; sourceTree = "<group>"; };
9657A6B21EDA63FC004461DE /* MotionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionContext.swift; sourceTree = "<group>"; };
966C539D1EDD207800A82A57 /* Motion+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+UIView.swift"; sourceTree = "<group>"; };
966C539F1EDD20DA00A82A57 /* Motion+UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+UIViewController.swift"; sourceTree = "<group>"; };
......@@ -47,7 +47,7 @@
966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCascadeDirection.swift; sourceTree = "<group>"; };
966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCoordinateSpace.swift; sourceTree = "<group>"; };
966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionPreprocessor.swift; sourceTree = "<group>"; };
966C53BA1EDD381E00A82A57 /* MotionCaptureSubviewPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCaptureSubviewPreprocessor.swift; sourceTree = "<group>"; };
966C53BA1EDD381E00A82A57 /* MotionSubviewPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionSubviewPreprocessor.swift; sourceTree = "<group>"; };
966C53BC1EDD396800A82A57 /* MotionAnimationFillMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationFillMode.swift; sourceTree = "<group>"; };
966C53BE1EDD399400A82A57 /* MotionAnimationTimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationTimingFunction.swift; sourceTree = "<group>"; };
96C98DD11E424AB000B22906 /* Motion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Motion.framework; sourceTree = BUILT_PRODUCTS_DIR; };
......@@ -91,29 +91,53 @@
96C98DE21E43809D00B22906 /* LICENSE */,
96C98DDD1E424B9000B22906 /* Info.plist */,
96C98DED1E438A5700B22906 /* Motion.h */,
961409A91E43CF1B00E7BA99 /* Motion+Obj-C.swift */,
966C539D1EDD207800A82A57 /* Motion+UIView.swift */,
966C539F1EDD20DA00A82A57 /* Motion+UIViewController.swift */,
966C53AE1EDD2F8B00A82A57 /* Motion+CALayer.swift */,
966C53B01EDD2FE600A82A57 /* Motion+CABasicAnimation.swift */,
964C153C1EDCF6EA00F0869D /* Motion.swift */,
96C98DE31E4382B100B22906 /* MotionController.swift */,
9657A6AB1EDA1601004461DE /* MotionObserver.swift */,
9657A6B21EDA63FC004461DE /* MotionContext.swift */,
9657A6AD1EDA19D8004461DE /* MotionAnimator.swift */,
96C98DE51E43848500B22906 /* MotionAnimation.swift */,
964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */,
966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */,
966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */,
966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */,
966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */,
966C53BA1EDD381E00A82A57 /* MotionCaptureSubviewPreprocessor.swift */,
966C53BC1EDD396800A82A57 /* MotionAnimationFillMode.swift */,
966C53BE1EDD399400A82A57 /* MotionAnimationTimingFunction.swift */,
96E846EB1EDD4FCF0005F32F /* Animator */,
96E846EC1EDD4FE40005F32F /* Extension */,
96E846EA1EDD4FB30005F32F /* Preprocessor */,
);
path = Sources;
sourceTree = "<group>";
};
96E846EA1EDD4FB30005F32F /* Preprocessor */ = {
isa = PBXGroup;
children = (
966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */,
966C53BA1EDD381E00A82A57 /* MotionSubviewPreprocessor.swift */,
);
name = Preprocessor;
sourceTree = "<group>";
};
96E846EB1EDD4FCF0005F32F /* Animator */ = {
isa = PBXGroup;
children = (
9657A6AD1EDA19D8004461DE /* MotionTransitionAnimator.swift */,
);
name = Animator;
sourceTree = "<group>";
};
96E846EC1EDD4FE40005F32F /* Extension */ = {
isa = PBXGroup;
children = (
961409A91E43CF1B00E7BA99 /* Motion+Obj-C.swift */,
966C539D1EDD207800A82A57 /* Motion+UIView.swift */,
966C539F1EDD20DA00A82A57 /* Motion+UIViewController.swift */,
966C53AE1EDD2F8B00A82A57 /* Motion+CALayer.swift */,
966C53B01EDD2FE600A82A57 /* Motion+CABasicAnimation.swift */,
);
name = Extension;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
......@@ -203,7 +227,7 @@
966C539E1EDD207800A82A57 /* Motion+UIView.swift in Sources */,
966C53B31EDD325B00A82A57 /* MotionSnapshot.swift in Sources */,
96C98DE61E43848500B22906 /* MotionAnimation.swift in Sources */,
9657A6AE1EDA19D8004461DE /* MotionAnimator.swift in Sources */,
9657A6AE1EDA19D8004461DE /* MotionTransitionAnimator.swift in Sources */,
9657A6AC1EDA1601004461DE /* MotionObserver.swift in Sources */,
9657A6B31EDA63FC004461DE /* MotionContext.swift in Sources */,
966C53B51EDD327D00A82A57 /* MotionCascadeDirection.swift in Sources */,
......@@ -215,7 +239,7 @@
966C53A01EDD20DA00A82A57 /* Motion+UIViewController.swift in Sources */,
96C98DE41E4382B100B22906 /* MotionController.swift in Sources */,
966C53BF1EDD399400A82A57 /* MotionAnimationTimingFunction.swift in Sources */,
966C53BB1EDD381E00A82A57 /* MotionCaptureSubviewPreprocessor.swift in Sources */,
966C53BB1EDD381E00A82A57 /* MotionSubviewPreprocessor.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -34,13 +34,12 @@ import UIKit
extension CALayer: CAAnimationDelegate {}
extension CALayer {
/**
A function that accepts CAAnimation objects and executes them on the
view's backing layer.
- Parameter animation: A CAAnimation instance.
*/
open func animate(_ animations: CAAnimation...) {
public func animate(_ animations: CAAnimation...) {
animate(animations)
}
......@@ -49,7 +48,7 @@ extension CALayer {
view's backing layer.
- Parameter animation: A CAAnimation instance.
*/
open func animate(_ animations: [CAAnimation]) {
public func animate(_ animations: [CAAnimation]) {
for animation in animations {
if nil == animation.delegate {
animation.delegate = self
......@@ -69,7 +68,7 @@ extension CALayer {
}
}
open func animationDidStart(_ anim: CAAnimation) {}
public func animationDidStart(_ anim: CAAnimation) {}
/**
A delegation function that is executed when the backing layer stops
......@@ -79,7 +78,7 @@ extension CALayer {
because it was completed or interrupted. True if completed, false
if interrupted.
*/
open func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
guard let a = anim as? CAPropertyAnimation else {
if let a = (anim as? CAAnimationGroup)?.animations {
for x in a {
......@@ -109,7 +108,7 @@ extension CALayer {
A function that accepts a list of MotionAnimation values and executes them.
- Parameter animations: A list of MotionAnimation values.
*/
open func motion(_ animations: MotionAnimation...) {
public func motion(_ animations: MotionAnimation...) {
motion(animations)
}
......@@ -118,7 +117,7 @@ extension CALayer {
- Parameter animations: An Array of MotionAnimation values.
- Parameter completion: An optional completion block.
*/
open func motion(_ animations: [MotionAnimation], completion: (() -> Void)? = nil) {
public func motion(_ animations: [MotionAnimation], completion: (() -> Void)? = nil) {
motion(delay: 0, duration: 0.35, timingFunction: .easeInEaseOut, animations: animations, completion: completion)
}
......@@ -137,6 +136,7 @@ extension CALayer {
switch v {
case let .delay(time):
t = time
default:break
}
}
......@@ -157,11 +157,14 @@ extension CALayer {
switch v {
case let .width(width):
w = width
case let .height(height):
h = height
case let .size(width, height):
w = width
h = height
default:break
}
}
......@@ -173,11 +176,14 @@ extension CALayer {
switch v {
case let .x(x):
px = x + w / 2
case let .y(y):
py = y + h / 2
case let .point(x, y):
px = x + w / 2
py = y + h / 2
default:break
}
}
......@@ -186,86 +192,120 @@ extension CALayer {
switch v {
case let .timingFunction(timingFunction):
tf = timingFunction
case let .duration(duration):
d = duration
case let .custom(animation):
a.append(animation)
case let .backgroundColor(color):
a.append(MotionBasicAnimation.background(color: color))
case let .barTintColor(color):
a.append(MotionBasicAnimation.barTint(color: color))
case let .borderColor(color):
a.append(MotionBasicAnimation.border(color: color))
case let .borderWidth(width):
a.append(MotionBasicAnimation.border(width: width))
case let .cornerRadius(radius):
a.append(MotionBasicAnimation.corner(radius: radius))
case let .transform(transform):
a.append(MotionBasicAnimation.transform(transform: transform))
case let .rotationAngle(angle):
let rotate = MotionBasicAnimation.rotation(angle: angle)
a.append(rotate)
case let .rotationAngleX(angle):
a.append(MotionBasicAnimation.rotationX(angle: angle))
case let .rotationAngleY(angle):
a.append(MotionBasicAnimation.rotationY(angle: angle))
case let .rotationAngleZ(angle):
a.append(MotionBasicAnimation.rotationZ(angle: angle))
case let .spin(rotations):
a.append(MotionBasicAnimation.spin(rotations: rotations))
case let .spinX(rotations):
a.append(MotionBasicAnimation.spinX(rotations: rotations))
case let .spinY(rotations):
a.append(MotionBasicAnimation.spinY(rotations: rotations))
case let .spinZ(rotations):
a.append(MotionBasicAnimation.spinZ(rotations: rotations))
case let .scale(to):
a.append(MotionBasicAnimation.scale(to: to))
case let .scaleX(to):
a.append(MotionBasicAnimation.scaleX(to: to))
case let .scaleY(to):
a.append(MotionBasicAnimation.scaleY(to: to))
case let .scaleZ(to):
a.append(MotionBasicAnimation.scaleZ(to: to))
case let .translate(x, y):
a.append(MotionBasicAnimation.translate(to: CGPoint(x: x, y: y)))
case let .translateX(to):
a.append(MotionBasicAnimation.translateX(to: to))
case let .translateY(to):
a.append(MotionBasicAnimation.translateY(to: to))
case let .translateZ(to):
a.append(MotionBasicAnimation.translateZ(to: to))
case .x(_), .y(_), .point(_, _):
let position = MotionBasicAnimation.position(to: CGPoint(x: px, y: py))
a.append(position)
case let .position(x, y):
a.append(MotionBasicAnimation.position(to: CGPoint(x: x, y: y)))
case let .fade(opacity):
let fade = MotionBasicAnimation.fade(opacity: opacity)
fade.fromValue = s.value(forKey: MotionAnimationKeyPath.opacity.rawValue) ?? NSNumber(floatLiteral: 1)
a.append(fade)
case let .zPosition(index):
let zPosition = MotionBasicAnimation.zPosition(index: index)
zPosition.fromValue = s.value(forKey: MotionAnimationKeyPath.zPosition.rawValue) ?? NSNumber(integerLiteral: 0)
a.append(zPosition)
case .width(_), .height(_), .size(_, _):
a.append(MotionBasicAnimation.size(CGSize(width: w, height: h)))
case let .shadowPath(path):
let shadowPath = MotionBasicAnimation.shadow(path: path)
shadowPath.fromValue = s.shadowPath
a.append(shadowPath)
case let .shadowOffset(offset):
let shadowOffset = MotionBasicAnimation.shadow(offset: offset)
shadowOffset.fromValue = s.shadowOffset
a.append(shadowOffset)
case let .shadowOpacity(opacity):
let shadowOpacity = MotionBasicAnimation.shadow(opacity: opacity)
shadowOpacity.fromValue = s.shadowOpacity
a.append(shadowOpacity)
case let .shadowRadius(radius):
let shadowRadius = MotionBasicAnimation.shadow(radius: radius)
shadowRadius.fromValue = s.shadowRadius
a.append(shadowRadius)
case let .depth(offset, opacity, radius):
if let path = s.shadowPath {
let shadowPath = MotionBasicAnimation.shadow(path: path)
......@@ -284,6 +324,7 @@ extension CALayer {
let shadowRadius = MotionBasicAnimation.shadow(radius: radius)
shadowRadius.fromValue = s.shadowRadius
a.append(shadowRadius)
default:break
}
}
......
......@@ -34,7 +34,10 @@ fileprivate var MotionInstanceControllerKey: UInt8 = 0
fileprivate struct MotionInstanceController {
/// A boolean indicating whether Motion is enabled.
fileprivate var isMotionEnabled: Bool
fileprivate var isEnabled: Bool
/// An optional reference to the current snapshot.
fileprivate var snapshot: UIView?
}
extension UIViewController {
......@@ -42,7 +45,7 @@ extension UIViewController {
fileprivate var motionControllerInstance: MotionInstanceController {
get {
return AssociatedObject.get(base: self, key: &MotionInstanceControllerKey) {
return MotionInstanceController(isMotionEnabled: false)
return MotionInstanceController(isEnabled: false, snapshot: nil)
}
}
set(value) {
......@@ -50,13 +53,23 @@ extension UIViewController {
}
}
/// An optional reference to the current snapshot.
internal var motionSnapshot: UIView? {
get {
return motionControllerInstance.snapshot
}
set(value) {
motionControllerInstance.snapshot = value
}
}
/// A boolean that indicates whether motion is enabled.
open var isMotionEnabled: Bool {
public var isMotionEnabled: Bool {
get {
return motionControllerInstance.isMotionEnabled
return motionControllerInstance.isEnabled
}
set(value) {
motionControllerInstance.isMotionEnabled = value
motionControllerInstance.isEnabled = value
}
}
}
......@@ -32,9 +32,76 @@ import UIKit
public typealias MotionDelayCancelBlock = (Bool) -> Void
open class Motion {
public class Motion: MotionController {
/// A reference to an optional transitioning context provided by UIKit.
fileprivate weak var transitionContext: UIViewControllerContextTransitioning?
/**
A boolean indicating if the transition view controller is a
UINavigationController.
*/
fileprivate var isNavigationController = false
/**
A boolean indicating if the transition view controller is a
UITabBarController.
*/
fileprivate var isTabBarController = false
/**
A boolean indicating if the transition view controller is a
UINavigationController or UITabBarController.
*/
fileprivate var isContainerController: Bool {
return isNavigationController || isTabBarController
}
/// A boolean indicating if the toView is at full screen.
fileprivate var isToViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == toViewController!.modalPresentationStyle || .overCurrentContext == toViewController!.modalPresentationStyle)
}
fileprivate var isFromViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == fromViewController!.modalPresentationStyle || .overCurrentContext == fromViewController!.modalPresentationStyle)
}
/// A reference to the fromViewController.view.
fileprivate var fromView: UIView {
return fromViewController!.view
}
/// A reference to the toViewController.view.
fileprivate var toView: UIView {
return toViewController!.view
}
/// A reference to the screen snapshot.
fileprivate var screenSnapshot: UIView!
/**
A reference to a shared Motion instance to control interactive
transitions.
*/
public static let shared = Motion()
/// A reference to the source view controller.
public fileprivate(set) var fromViewController: UIViewController?
/// A reference to the destination view controller.
public fileprivate(set) var toViewController: UIViewController?
/// A boolean indicating if the view controller is presenting.
public fileprivate(set) var isPresenting = true
/// A reference to the animation elapsed time.
public override var elapsedTime: TimeInterval {
didSet {
guard isTransitioning else {
return
}
transitionContext?.updateInteractiveTransition(CGFloat(elapsedTime))
}
}
}
extension Motion {
......@@ -46,13 +113,14 @@ extension Motion {
the animations have completed.
*/
@discardableResult
open class func delay(_ time: TimeInterval, execute block: @escaping () -> Void) -> MotionDelayCancelBlock? {
public class func delay(_ time: TimeInterval, execute block: @escaping () -> Void) -> MotionDelayCancelBlock? {
var cancelable: MotionDelayCancelBlock?
let delayed: MotionDelayCancelBlock = {
if !$0 {
DispatchQueue.main.async(execute: block)
}
cancelable = nil
}
......@@ -69,7 +137,7 @@ extension Motion {
Cancels the delayed MotionDelayCancelBlock.
- Parameter delayed completion: An MotionDelayCancelBlock.
*/
open class func cancel(delayed completion: MotionDelayCancelBlock) {
public class func cancel(delayed completion: MotionDelayCancelBlock) {
completion(true)
}
......@@ -77,7 +145,7 @@ extension Motion {
Disables the default animations set on CALayers.
- Parameter animations: A callback that wraps the animations to disable.
*/
open class func disable(_ animations: (() -> Void)) {
public class func disable(_ animations: (() -> Void)) {
animate(duration: 0, animations: animations)
}
......@@ -89,7 +157,7 @@ extension Motion {
- Parameter completion: A completion block that is executed once
the animations have completed.
*/
open class func animate(duration: CFTimeInterval, timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, animations: (() -> Void), completion: (() -> Void)? = nil) {
public class func animate(duration: CFTimeInterval, timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, animations: (() -> Void), completion: (() -> Void)? = nil) {
CATransaction.begin()
CATransaction.setAnimationDuration(duration)
CATransaction.setCompletionBlock(completion)
......@@ -105,7 +173,7 @@ extension Motion {
- Parameter duration: An animation duration time for the group.
- Returns: A CAAnimationGroup.
*/
open class func animate(group animations: [CAAnimation], timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, duration: CFTimeInterval = 0.5) -> CAAnimationGroup {
public class func animate(group animations: [CAAnimation], timingFunction: MotionAnimationTimingFunction = .easeInEaseOut, duration: CFTimeInterval = 0.5) -> CAAnimationGroup {
let group = CAAnimationGroup()
group.fillMode = MotionAnimationFillModeToValue(mode: .both)
group.isRemovedOnCompletion = false
......@@ -115,3 +183,86 @@ extension Motion {
return group
}
}
extension Motion {
/// Prepares the screen snapshot.
fileprivate func prepareScreenSnapshot() {
screenSnapshot?.removeFromSuperview()
screenSnapshot = (transitionContainer.window ?? fromView).snapshotView(afterScreenUpdates: true)
(transitionContainer.window ?? transitionContainer)?.addSubview(screenSnapshot)
}
/// Prepares the preprocessors.
fileprivate func preparePreprocessors() {
preprocessors = [MotionSubviewPreprocessor()]
}
/// Prepares the animators.
fileprivate func prepareAnimators() {
animators = []
}
/// Prepares the transitionContainer.
fileprivate func prepareTransitionContainer() {
transitionContainer.isUserInteractionEnabled = false
}
/// Prepares the container.
fileprivate func prepareContainer() {
container = UIView(frame: transitionContainer.bounds)
transitionContainer.addSubview(container)
}
/// Prepares the context.
fileprivate func prepareContext() {
context = MotionContext(container: container)
container.addSubview(toView)
container.addSubview(fromView)
context.set(fromViews: subviews(of: fromView), toViews: subviews(of: toView))
}
/// Prepares the toView.
fileprivate func prepareToView() {
toView.frame = fromView.frame
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
}
extension Motion {
/**
Removes a snapshot from a given view controller.
- Parameter for viewController: A UIViewController.
*/
fileprivate func removeSnapshot(for viewController: UIViewController?) {
guard let v = viewController?.motionSnapshot else {
return
}
v.removeFromSuperview()
viewController?.motionSnapshot = nil
}
}
extension Motion {
/// Starts the transition.
fileprivate func start() {
guard isTransitioning else {
return
}
removeSnapshot(for: fromViewController)
removeSnapshot(for: toViewController)
prepareScreenSnapshot()
preparePreprocessors()
prepareAnimators()
prepareTransitionContainer()
prepareContainer()
prepareToView()
prepareContext()
}
}
......@@ -87,7 +87,7 @@ extension MotionContext {
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
fileprivate func set(fromViews: [UIView], toViews: [UIView]) {
internal func set(fromViews: [UIView], toViews: [UIView]) {
self.fromViews = fromViews
self.toViews = toViews
prepare(views: fromViews, identifierIndex: &sourceIdentifierToView)
......
......@@ -30,7 +30,7 @@
import UIKit
open class MotionController: NSObject, MotionSubscriber {
public class MotionController: NSObject, MotionSubscriber {
/// An optional reference to the animation display link.
fileprivate var displayLink: CADisplayLink? {
willSet {
......@@ -52,18 +52,18 @@ open class MotionController: NSObject, MotionSubscriber {
}
/// A reference to the animation duration.
fileprivate var transitionDuration: TimeInterval = 0
fileprivate var duration: TimeInterval = 0
/**
A reference to the animation total duration,
which is the total running animation time.
*/
fileprivate var transitionTotalDuration: TimeInterval = 0
fileprivate var totalDuration: TimeInterval = 0
/// A reference to the animation start time.
fileprivate var transitionStartTime: TimeInterval? {
fileprivate var startTime: TimeInterval? {
didSet {
guard nil != transitionStartTime else {
guard nil != startTime else {
displayLink = nil
return
}
......@@ -77,7 +77,7 @@ open class MotionController: NSObject, MotionSubscriber {
}
/// A reference to the animation elapsed time.
open fileprivate(set) var transitionElapsedTime: TimeInterval = 0 {
public internal(set) var elapsedTime: TimeInterval = 0 {
didSet {
guard isTransitioning else {
return
......@@ -88,48 +88,46 @@ open class MotionController: NSObject, MotionSubscriber {
}
}
/// A reference to a MotionContext.
public internal(set) var context: MotionContext!
/// A reference to an Array of MotionObservers.
open fileprivate(set) var observers = [MotionObserver]()
public internal(set) var observers = [MotionObserver]()
/// A reference to an Array of MotionAnimators.
open fileprivate(set) var animators = [MotionAnimator]()
/// A reference to an Array of MotionTransitionAnimator.
public internal(set) var animators = [MotionTransitionAnimator]()
/// A reference to the preprocessors.
open fileprivate(set) var processors = [MotionTransitionPreprocessor]()
public internal(set) var preprocessors = [MotionTransitionPreprocessor]()
/// A boolean indicating if a transition is in progress.
open var isTransitioning: Bool {
public var isTransitioning: Bool {
return nil == transitionContainer
}
/// A boolean indicating if the animation is finished.
open fileprivate(set) var isFinished = false
public fileprivate(set) var isFinished = false
/// A boolean indicating if the animation is interactive.
open var isInteractive: Bool {
public var isInteractive: Bool {
return nil == displayLink
}
/**
An animation container used within the transitionContainer
during a transition.
*/
public internal(set) var container: UIView!
/// Transition container.
open fileprivate(set) var transitionContainer: UIView!
public fileprivate(set) var transitionContainer: UIView!
/// An Array of from and to view paris to be animated.
open fileprivate(set) var transitionParis = [(fromViews: [UIView], toViews: [UIView])]()
public fileprivate(set) var transitionParis = [(fromViews: [UIView], toViews: [UIView])]()
}
extension MotionController {
/**
Retrieves all the subviews of a given view.
- Parameter of view: A UIView.
- Returns: An Array of UIViews.
*/
fileprivate func subviews(of view: UIView) -> [UIView] {
var views: [UIView] = []
subviews(of: view, views: &views)
return views
}
/**
Populates an Array of UIViews with the subviews of a given view.
- Parameter of view: A UIView.
- Returns: An Array of UIViews.
......@@ -142,6 +140,17 @@ extension MotionController {
subviews(of: v, views: &views)
}
}
/**
Retrieves all the subviews of a given view.
- Parameter of view: A UIView.
- Returns: An Array of UIViews.
*/
internal func subviews(of view: UIView) -> [UIView] {
var views: [UIView] = []
subviews(of: view, views: &views)
return views
}
}
extension MotionController {
......@@ -162,28 +171,28 @@ extension MotionController {
return
}
guard 0 < transitionDuration else {
guard 0 < duration else {
return
}
guard let v = transitionStartTime else {
guard let v = startTime else {
return
}
var elapsedTime = CACurrentMediaTime() - v
var t = CACurrentMediaTime() - v
if elapsedTime > transitionDuration {
transitionElapsedTime = isFinished ? 1 : 0
if t > duration {
elapsedTime = isFinished ? 1 : 0
completeTransition()
} else {
elapsedTime = elapsedTime / transitionDuration
t = t / duration
if !isFinished {
elapsedTime = 1 - elapsedTime
t = 1 - t
}
transitionElapsedTime = max(0, min(1, elapsedTime))
elapsedTime = max(0, min(1, t))
}
}
}
......@@ -191,16 +200,16 @@ extension MotionController {
extension MotionController {
fileprivate func updateMotionObservers() {
for v in observers {
v.update(elapsedTime: transitionElapsedTime)
v.update(elapsedTime: elapsedTime)
}
}
/// Updates the motion animators.
fileprivate func updateMotionAnimators() {
let elapsedTime = transitionElapsedTime * transitionTotalDuration
let t = elapsedTime * totalDuration
for v in animators {
v.seekTo(elapsedTime: elapsedTime)
v.seekTo(elapsedTime: t)
}
}
}
......@@ -221,8 +230,8 @@ extension MotionController {
/// Cleans the transition values.
fileprivate func cleanTransitionValues() {
transitionStartTime = nil
transitionElapsedTime = 0
transitionTotalDuration = 0
startTime = nil
elapsedTime = 0
totalDuration = 0
}
}
......@@ -30,7 +30,7 @@
import UIKit
public class MotionCaptureSubviewPreprocessor: MotionTransitionPreprocessor {
public class MotionSubviewPreprocessor: MotionTransitionPreprocessor {
/// A reference to a MotionContext.
public weak var context: MotionContext!
......
......@@ -30,7 +30,7 @@
import UIKit
public protocol MotionAnimator {
public protocol MotionTransitionAnimator {
/// A reference to a MotionContext.
weak var context: MotionContext! { get set }
......
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