Commit b205f8d1 by Daniel Dahan

added base transitioning delegation handlers

parent ddf83ac5
...@@ -95,10 +95,10 @@ ...@@ -95,10 +95,10 @@
96C98DED1E438A5700B22906 /* Motion.h */, 96C98DED1E438A5700B22906 /* Motion.h */,
964C153C1EDCF6EA00F0869D /* Motion.swift */, 964C153C1EDCF6EA00F0869D /* Motion.swift */,
96C98DE31E4382B100B22906 /* MotionController.swift */, 96C98DE31E4382B100B22906 /* MotionController.swift */,
96E846F51EDDA7F20005F32F /* MotionTransitionState.swift */,
9657A6AB1EDA1601004461DE /* MotionObserver.swift */, 9657A6AB1EDA1601004461DE /* MotionObserver.swift */,
9657A6B21EDA63FC004461DE /* MotionContext.swift */, 9657A6B21EDA63FC004461DE /* MotionContext.swift */,
96C98DE51E43848500B22906 /* MotionAnimation.swift */, 96C98DE51E43848500B22906 /* MotionAnimation.swift */,
96E846F51EDDA7F20005F32F /* MotionTransitionState.swift */,
964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */, 964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */,
966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */, 966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */,
966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */, 966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */,
......
...@@ -340,10 +340,10 @@ public struct MotionBasicAnimation { ...@@ -340,10 +340,10 @@ public struct MotionBasicAnimation {
/** /**
Creates a CABasicAnimation for the opacity key path. Creates a CABasicAnimation for the opacity key path.
- Parameter opacity: A Double. - Parameter to opacity: A Double.
- Returns: A CABasicAnimation. - Returns: A CABasicAnimation.
*/ */
public static func fade(opacity: Double) -> CABasicAnimation { public static func fade(to opacity: Double) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: .opacity) let animation = CABasicAnimation(keyPath: .opacity)
animation.toValue = NSNumber(floatLiteral: opacity) animation.toValue = NSNumber(floatLiteral: opacity)
return animation return animation
......
...@@ -274,7 +274,7 @@ extension CALayer { ...@@ -274,7 +274,7 @@ extension CALayer {
a.append(MotionBasicAnimation.position(to: CGPoint(x: x, y: y))) a.append(MotionBasicAnimation.position(to: CGPoint(x: x, y: y)))
case let .fade(opacity): case let .fade(opacity):
let fade = MotionBasicAnimation.fade(opacity: opacity) let fade = MotionBasicAnimation.fade(to: opacity)
fade.fromValue = s.value(forKey: MotionAnimationKeyPath.opacity.rawValue) ?? NSNumber(floatLiteral: 1) fade.fromValue = s.value(forKey: MotionAnimationKeyPath.opacity.rawValue) ?? NSNumber(floatLiteral: 1)
a.append(fade) a.append(fade)
......
...@@ -38,6 +38,12 @@ fileprivate struct MotionInstanceController { ...@@ -38,6 +38,12 @@ fileprivate struct MotionInstanceController {
/// An optional reference to the current snapshot. /// An optional reference to the current snapshot.
fileprivate var snapshot: UIView? fileprivate var snapshot: UIView?
/// An optional reference to the previous UINavigationControllerDelegate.
fileprivate var previousNavigationDelegate: UINavigationControllerDelegate?
/// An optional reference to the previous UITabBarControllerDelegate.
fileprivate var previousTabBarDelegate: UITabBarControllerDelegate?
} }
extension UIViewController { extension UIViewController {
...@@ -45,7 +51,7 @@ extension UIViewController { ...@@ -45,7 +51,7 @@ extension UIViewController {
fileprivate var motionControllerInstance: MotionInstanceController { fileprivate var motionControllerInstance: MotionInstanceController {
get { get {
return AssociatedObject.get(base: self, key: &MotionInstanceControllerKey) { return AssociatedObject.get(base: self, key: &MotionInstanceControllerKey) {
return MotionInstanceController(isEnabled: false, snapshot: nil) return MotionInstanceController(isEnabled: false, snapshot: nil, previousNavigationDelegate: nil, previousTabBarDelegate: nil)
} }
} }
set(value) { set(value) {
...@@ -57,10 +63,35 @@ extension UIViewController { ...@@ -57,10 +63,35 @@ extension UIViewController {
@IBInspectable @IBInspectable
public var isMotionEnabled: Bool { public var isMotionEnabled: Bool {
get { get {
return motionControllerInstance.isEnabled return transitioningDelegate is Motion
} }
set(value) { set {
motionControllerInstance.isEnabled = value guard newValue != isMotionEnabled else {
return
}
if newValue {
transitioningDelegate = Motion.shared
if let v = self as? UINavigationController {
motionPreviousNavigationDelegate = v.delegate
v.delegate = Motion.shared
}
if let v = self as? UITabBarController {
motionPreviousTabBarDelegate = v.delegate
v.delegate = Motion.shared
}
} else {
transitioningDelegate = nil
if let v = self as? UINavigationController, v.delegate is Motion {
v.delegate = motionPreviousNavigationDelegate
}
if let v = self as? UITabBarController, v.delegate is Motion {
v.delegate = motionPreviousTabBarDelegate
}
}
} }
} }
...@@ -73,4 +104,24 @@ extension UIViewController { ...@@ -73,4 +104,24 @@ extension UIViewController {
motionControllerInstance.snapshot = value motionControllerInstance.snapshot = value
} }
} }
/// An optional reference to the previous UINavigationControllerDelegate.
internal var motionPreviousNavigationDelegate: UINavigationControllerDelegate? {
get {
return motionControllerInstance.previousNavigationDelegate
}
set(value) {
motionControllerInstance.previousNavigationDelegate = value
}
}
/// An optional reference to the previous UITabBarControllerDelegate.
internal var motionPreviousTabBarDelegate: UITabBarControllerDelegate? {
get {
return motionControllerInstance.previousTabBarDelegate
}
set(value) {
motionControllerInstance.previousTabBarDelegate = value
}
}
} }
...@@ -36,6 +36,9 @@ public class Motion: MotionController { ...@@ -36,6 +36,9 @@ public class Motion: MotionController {
/// A reference to an optional transitioning context provided by UIKit. /// A reference to an optional transitioning context provided by UIKit.
fileprivate weak var transitionContext: UIViewControllerContextTransitioning? fileprivate weak var transitionContext: UIViewControllerContextTransitioning?
/// A boolean inficating whether or not the transition is animating.
fileprivate var isAnimating = false
/** /**
A boolean indicating if the transition view controller is a A boolean indicating if the transition view controller is a
UINavigationController. UINavigationController.
...@@ -222,12 +225,6 @@ extension Motion { ...@@ -222,12 +225,6 @@ extension Motion {
transitionContainer.isUserInteractionEnabled = false transitionContainer.isUserInteractionEnabled = false
} }
/// Prepares the container.
fileprivate func prepareContainer() {
container = UIView(frame: transitionContainer.bounds)
transitionContainer.addSubview(container)
}
/// Prepares the context. /// Prepares the context.
fileprivate func prepareContext() { fileprivate func prepareContext() {
context = MotionContext(container: container) context = MotionContext(container: container)
...@@ -276,12 +273,142 @@ extension Motion { ...@@ -276,12 +273,142 @@ extension Motion {
preparePreprocessors() preparePreprocessors()
prepareAnimators() prepareAnimators()
prepareTransitionContainer() prepareTransitionContainer()
prepareContainer()
prepareToView() prepareToView()
prepareContext() prepareContext()
processContext() processContext()
} }
fileprivate func completed(isFinished: Bool) {
transitionContext?.completeTransition(!isFinished)
}
}
extension Motion {
/**
Helper transition function.
- Parameter from: A UIViewController.
- Parameter to: A UIViewController.
- Parameter in container: A UIView.
- Parameter completion: A completion block.
*/
fileprivate func transition(from: UIViewController, to: UIViewController, in container: UIView, completion: ((Bool) -> Void)? = nil) {
guard !isTransitioning else {
return
}
isPresenting = true
transitionContainer = container
fromViewController = from
toViewController = to
start()
}
}
extension Motion: UIViewControllerAnimatedTransitioning {
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
public func animateTransition(using context: UIViewControllerContextTransitioning) {
guard !isTransitioning else {
return
}
transitionContext = context
fromViewController = fromViewController ?? context.viewController(forKey: .from)
toViewController = toViewController ?? context.viewController(forKey: .to)
transitionContainer = context.containerView
start()
}
/**
Returns the transition duration time interval.
- Parameter using transitionContext: An optional UIViewControllerContextTransitioning.
- Returns: A TimeInterval that is the total animation time including delays.
*/
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0 // Will be updated dynamically.
}
public func animationEnded(_ transitionCompleted: Bool) {
isAnimating = !transitionCompleted
}
}
extension Motion: UIViewControllerTransitioningDelegate {
var interactiveTransitioning: UIViewControllerInteractiveTransitioning? {
return self
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = true
self.fromViewController = fromViewController ?? presenting
self.toViewController = toViewController ?? presented
return self
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = false
self.fromViewController = fromViewController ?? dismissed
return self
}
public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
}
extension Motion: UIViewControllerInteractiveTransitioning {
public var wantsInteractiveStart: Bool {
return true
}
public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
animateTransition(using: transitionContext)
}
}
extension Motion: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = .push == operation
self.fromViewController = fromViewController ?? fromVC
self.toViewController = toViewController ?? toVC
self.isNavigationController = true
return self
}
public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
}
extension Motion: UITabBarControllerDelegate {
public func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
return !isAnimating
}
public func tabBarController(_ tabBarController: UITabBarController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
public func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isAnimating = true
let fromVCIndex = tabBarController.childViewControllers.index(of: fromVC)!
let toVCIndex = tabBarController.childViewControllers.index(of: toVC)!
self.isPresenting = toVCIndex > fromVCIndex
self.fromViewController = fromViewController ?? fromVC
self.toViewController = toViewController ?? toVC
self.isTabBarController = true
return self
}
} }
...@@ -113,17 +113,22 @@ public class MotionController: NSObject, MotionSubscriber { ...@@ -113,17 +113,22 @@ public class MotionController: NSObject, MotionSubscriber {
return nil == displayLink return nil == displayLink
} }
/// An Array of from and to view paris to be animated.
public fileprivate(set) var transitionParis = [(fromViews: [UIView], toViews: [UIView])]()
/** /**
An animation container used within the transitionContainer An animation container used within the transitionContainer
during a transition. during a transition.
*/ */
public internal(set) var container: UIView! public fileprivate(set) var container: UIView!
/// Transition container. /// Transition container.
public fileprivate(set) var transitionContainer: UIView! public internal(set) var transitionContainer: UIView! {
didSet {
/// An Array of from and to view paris to be animated. container = UIView(frame: transitionContainer.bounds)
public fileprivate(set) var transitionParis = [(fromViews: [UIView], toViews: [UIView])]() transitionContainer.addSubview(container)
}
}
} }
extension MotionController { extension MotionController {
......
...@@ -45,6 +45,17 @@ public final class MotionTransitionAnimation { ...@@ -45,6 +45,17 @@ public final class MotionTransitionAnimation {
extension MotionTransitionAnimation { extension MotionTransitionAnimation {
/** /**
Animates the view with a matching motion identifier.
- Parameter _ identifier: A String.
- Returns: A MotionTransitionAnimation.
*/
public static func motionIdentifier(_ identifier: String) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.motionIdentifier = identifier
}
}
/**
Animates the view's current background color to the Animates the view's current background color to the
given color. given color.
- Parameter color: A UIColor. - Parameter color: A UIColor.
...@@ -215,10 +226,10 @@ extension MotionTransitionAnimation { ...@@ -215,10 +226,10 @@ extension MotionTransitionAnimation {
/** /**
Animates the view's current opacity to the given one. Animates the view's current opacity to the given one.
- Parameter _ opacity: A Float value. - Parameter to opacity: A Float value.
- Returns: A MotionTransitionAnimation. - Returns: A MotionTransitionAnimation.
*/ */
public static func opacity(_ opacity: Float) -> MotionTransitionAnimation { public static func fade(to opacity: Float) -> MotionTransitionAnimation {
return MotionTransitionAnimation { return MotionTransitionAnimation {
$0.opacity = opacity $0.opacity = opacity
} }
...@@ -300,4 +311,104 @@ extension MotionTransitionAnimation { ...@@ -300,4 +311,104 @@ extension MotionTransitionAnimation {
$0.shadowRadius = radius $0.shadowRadius = radius
} }
} }
/**
Animates the view's contents rect to the given one.
- Parameter rect: A CGRect.
- Returns: A MotionTransitionAnimation.
*/
public static func contents(rect: CGRect) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.contentsRect = rect
}
}
/**
Animates the view's contents scale to the given one.
- Parameter scale: A CGFloat.
- Returns: A MotionTransitionAnimation.
*/
public static func contents(scale: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.contentsScale = scale
}
}
/**
The duration of the view's animation.
- Parameter _ duration: A TimeInterval.
- Returns: A MotionTransitionAnimation.
*/
public static func duration(_ duration: TimeInterval) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.duration = duration
}
}
/**
Sets the view's animation duration to the longest
running animation within a transition.
*/
public static var preferredDurationMatchesLongest = MotionTransitionAnimation.duration(.infinity)
/**
Delays the animation of a given view.
- Parameter _ time: TimeInterval.
- Returns: A MotionTransitionAnimation.
*/
public static func delay(_ time: TimeInterval) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.delay = time
}
}
/**
Sets the view's timing function for the animation.
- Parameter _ timingFunction: A MotionAnimationTimingFunction.
- Returns: A MotionTransitionAnimation.
*/
public static func timingFunction(_ timingFunction: MotionAnimationTimingFunction) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.timingFunction = timingFunction
}
}
/**
Available in iOS 9+, animates a view using the spring API,
given a stiffness and damping.
- Parameter stiffness: A CGFlloat.
- Parameter damping: A CGFloat.
- Returns: A MotionTransitionAnimation.
*/
@available(iOS 9, *)
public static func spring(stiffness: CGFloat, damping: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.spring = (stiffness, damping)
}
}
/**
Animates the natural curve of a view. A value of 1 represents
a curve in a downward direction, and a value of -1
represents a curve in an upward direction.
- Parameter intensity: A CGFloat.
*/
public static func arc(intensity: CGFloat = 1) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.arc = intensity
}
}
/**
Animates subviews with an increasing delay between each animation.
- Parameter delta: A TimeInterval.
- Parameter direction: A MotionCascadeDirection.
- Paramater animationDelayUntilMatchedViews: A boolean indicating whether
or not to delay the subview animation until all have started.
*/
public static func cascade(delta: TimeInterval = 0.02, direction: MotionCascadeDirection = .topToBottom, animationDelayUntilMatchedViews: Bool = false) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
$0.cascade = (delta, direction, animationDelayUntilMatchedViews)
}
}
} }
...@@ -30,5 +30,87 @@ ...@@ -30,5 +30,87 @@
import UIKit import UIKit
internal class MotionTransitionStateWrapper {
/// A reference to a MotionTransitionAnimationState.
internal var state: MotionTransitionState
/**
An initializer that accepts a given MotionTransitionAnimationState.
- Parameter state: A MotionTransitionAnimationState.
*/
internal init(state: MotionTransitionState) {
self.state = state
}
}
public struct MotionTransitionState {
/// An optional reference to the start state of the view.
internal var startState: MotionTransitionStateWrapper?
/// A reference to the motion identifier.
public var motionIdentifier: String?
public var startStateIfMatched: [MotionTransitionAnimation]?
public var position: CGPoint?
public var size: CGSize?
public var transform: CATransform3D?
public var opacity: Float?
public var cornerRadius: CGFloat?
public var backgroundColor: CGColor?
public var zPosition: CGFloat?
public var contentsRect: CGRect?
public var contentsScale: CGFloat?
public var borderWidth: CGFloat?
public var borderColor: CGColor?
public var shadowColor: CGColor?
public var shadowOpacity: Float?
public var shadowOffset: CGSize?
public var shadowRadius: CGFloat?
public var shadowPath: CGPath?
public var masksToBounds: Bool?
public var displayShadow: Bool = true
public var overlay: (color: CGColor, opacity: CGFloat)?
public var spring: (CGFloat, CGFloat)?
public var delay: TimeInterval = 0
public var duration: TimeInterval?
public var timingFunction: MotionAnimationTimingFunction?
public var arc: CGFloat?
public var cascade: (TimeInterval, MotionCascadeDirection, Bool)?
public var ignoreSubviewTransitionAnimations: Bool?
public var coordinateSpace: MotionCoordinateSpace?
public var useScaleBasedSizeChange: Bool?
public var snapshotType: MotionSnapshot?
public var forceAnimate: Bool = false
public var custom: [String:Any]?
init(transitionAnimations: [MotionTransitionAnimation]) {
append(contentsOf: transitionAnimations)
}
public mutating func append(_ transitionAnimations: MotionTransitionAnimation) {
transitionAnimations.apply(&self)
}
public mutating func append(contentsOf transitionAnimations: [MotionTransitionAnimation]) {
for v in transitionAnimations {
v.apply(&self)
}
}
}
extension MotionTransitionState: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: MotionTransitionAnimation...) {
append(contentsOf: elements)
}
}
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