Commit 92f3daab by Daniel Dahan

development: refined PresentedMotion phase 1

parent d50305a1
......@@ -344,7 +344,7 @@ public protocol MotionDelegate {
optional func motionDelayTransitionByTimeInterval(motion: Motion) -> TimeInterval
}
open class Motion: NSObject {
open class MotionAnimator: NSObject {
/// A boolean indicating whether Motion is presenting a view controller.
open fileprivate(set) var isPresenting: Bool
......@@ -408,11 +408,6 @@ open class Motion: NSObject {
return Motion.subviews(of: fromView)
}
/// A time value to delay the transition animation by.
fileprivate var delayTransitionByTimeInterval: TimeInterval {
return fromViewController.motionDelegate?.motionDelayTransitionByTimeInterval?(motion: self) ?? 0
}
/// The default initializer.
public override init() {
isPresenting = false
......@@ -515,7 +510,7 @@ open class Motion: NSObject {
}
}
extension Motion: UIViewControllerAnimatedTransitioning {
extension MotionAnimator: UIViewControllerAnimatedTransitioning {
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
......@@ -523,21 +518,6 @@ extension Motion: UIViewControllerAnimatedTransitioning {
@objc(animateTransition:)
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext
fromViewController.motionDelegate?.motion?(motion: self, willTransition: fromView, toView: toView)
Motion.delay(delayTransitionByTimeInterval) { [weak self] in
guard let s = self else {
return
}
s.prepareContainerView()
s.prepareTransitionSnapshot()
s.prepareTransitionPairs()
s.prepareTransitionView()
s.prepareTransitionBackgroundView()
s.prepareToView()
s.prepareTransitionAnimation()
}
}
/**
......@@ -551,66 +531,7 @@ extension Motion: UIViewControllerAnimatedTransitioning {
}
}
extension Motion {
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
}
/// Prepares the transitionSnapshot.
fileprivate func prepareTransitionSnapshot() {
transitionSnapshot = fromView.transitionSnapshot(afterUpdates: true, shouldHide: false)
transitionSnapshot.frame = fromView.frame
containerView.addSubview(transitionSnapshot)
}
/// Prepares the transitionPairs.
fileprivate func prepareTransitionPairs() {
for from in fromSubviews {
for to in toSubviews {
guard to.motionIdentifier == from.motionIdentifier else {
continue
}
transitionPairs.append((from, to))
}
}
}
/// Prepares the transitionView.
fileprivate func prepareTransitionView() {
transitionView.frame = toView.bounds
transitionView.isUserInteractionEnabled = false
containerView.insertSubview(transitionView, belowSubview: transitionSnapshot)
}
/// Prepares the transitionBackgroundView.
fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = isPresenting ? .clear : fromView.backgroundColor ?? .clear
transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView)
}
/// Prepares the toView.
fileprivate func prepareToView() {
toView.isHidden = isPresenting
containerView.insertSubview(toView, belowSubview: transitionView)
toView.frame = fromView.frame
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
/// Prepares the transition animation.
fileprivate func prepareTransitionAnimation() {
addTransitionAnimations()
addBackgroundAnimation()
cleanUpAnimation()
removeTransitionSnapshot()
}
}
extension Motion {
extension MotionAnimator {
/// Adds the available transition animations.
fileprivate func addTransitionAnimations() {
for (from, to) in transitionPairs {
......@@ -686,26 +607,7 @@ extension Motion {
}
}
extension Motion {
/**
Creates a CAAnimationGroup.
- Parameter animations: An Array of CAAnimation objects.
- Parameter timingFunction: An MotionAnimationTimingFunction value.
- 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 {
let group = CAAnimationGroup()
group.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
group.isRemovedOnCompletion = false
group.animations = animations
group.duration = duration
group.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: timingFunction)
return group
}
}
extension Motion {
extension MotionAnimator {
/**
Calculates the animation delay time based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
......@@ -764,126 +666,20 @@ extension Motion {
}
}
extension Motion {
/// Cleans up the animation transition.
fileprivate func cleanUpAnimation() {
Motion.delay(transitionDuration(using: transitionContext) + delayTransitionByTimeInterval) { [weak self] in
guard let s = self else {
return
}
s.showToSubviews()
s.removeTransitionView()
s.clearTransitionPairs()
s.completeTransition()
}
}
/// Removes the transitionSnapshot from its superview.
fileprivate func removeTransitionSnapshot() {
Motion.delay(delay) { [weak self] in
self?.transitionSnapshot.removeFromSuperview()
}
}
/// Shows the toView and its subviews.
fileprivate func showToSubviews() {
toSubviews.forEach {
$0.isHidden = false
}
toView.isHidden = false
}
/// Clears the transitionPairs Array.
fileprivate func clearTransitionPairs() {
transitionPairs.removeAll()
}
/// Removes the transitionView.
fileprivate func removeTransitionView() {
transitionView.removeFromSuperview()
}
/// Calls the completionTransition method.
fileprivate func completeTransition() {
toViewController.motionDelegate?.motion?(motion: self, didTransition: fromView, toView: toView)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
open class PresentedMotion: NSObject {
/**
An Array of UIView pairs with common motionIdentifiers in
the from and to view controllers.
*/
open fileprivate(set) var transitionPairs = [(UIView, UIView)]()
/// A reference to the transition snapshot.
open var transitionSnapshot: UIView!
/// A reference to the transition background view.
open let transitionBackgroundView = UIView()
/// A reference to the view controller that is being transitioned to.
open var toViewController: UIViewController {
return transitionContext.viewController(forKey: .to)!
}
/// A reference to the view controller that is being transitioned from.
open var fromViewController: UIViewController {
return transitionContext.viewController(forKey: .from)!
}
/// The transition context for the current transition.
open var transitionContext: UIViewControllerContextTransitioning!
/// The transition delay time.
open var delay: TimeInterval = 0
/// The transition duration time.
open var duration: TimeInterval = 0.35
/// The transition container view.
open var containerView: UIView!
/// The view that is used to animate the transitions between view controllers.
open var transitionView = UIView()
/// The view that is being transitioned to.
open var toView: UIView {
return toViewController.view
}
/// The subviews of the view being transitioned to.
open var toSubviews: [UIView] {
return Motion.subviews(of: toView)
}
/// The view that is being transitioned from.
open var fromView: UIView {
return fromViewController.view
}
/// The subviews of the view being transitioned from.
open var fromSubviews: [UIView] {
return Motion.subviews(of: fromView)
}
open class Motion: MotionAnimator {
/// A time value to delay the transition animation by.
fileprivate var delayTransitionByTimeInterval: TimeInterval {
return 0
// return fromViewController.motionDelegate?.motionDelayTransitionByTimeInterval?(motion: self) ?? 0
return fromViewController.motionDelegate?.motionDelayTransitionByTimeInterval?(motion: self) ?? 0
}
}
extension PresentedMotion: UIViewControllerAnimatedTransitioning {
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
@objc(animateTransition:)
open func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext
open override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
super.animateTransition(using: transitionContext)
fromViewController.motionDelegate?.motion?(motion: self, willTransition: fromView, toView: toView)
Motion.delay(delayTransitionByTimeInterval) { [weak self] in
guard let s = self else {
......@@ -899,19 +695,9 @@ extension PresentedMotion: UIViewControllerAnimatedTransitioning {
s.prepareTransitionAnimation()
}
}
/**
Returns the transition duration time interval.
- Parameter using transitionContext: An optional UIViewControllerContextTransitioning.
- Returns: A TimeInterval that is the total animation time including delays.
*/
@objc(transitionDuration:)
open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.35
}
}
extension PresentedMotion {
extension Motion {
/// Prepares the containerView.
fileprivate func prepareContainerView() {
containerView = transitionContext.containerView
......@@ -945,15 +731,14 @@ extension PresentedMotion {
/// Prepares the transitionBackgroundView.
fileprivate func prepareTransitionBackgroundView() {
transitionBackgroundView.backgroundColor = .clear
transitionBackgroundView.backgroundColor = isPresenting ? .clear : fromView.backgroundColor ?? .clear
transitionBackgroundView.frame = transitionView.bounds
transitionView.addSubview(transitionBackgroundView)
}
/// Prepares the toView.
fileprivate func prepareToView() {
toView.isHidden = true
toView.isHidden = isPresenting
containerView.insertSubview(toView, belowSubview: transitionView)
toView.frame = fromView.frame
......@@ -971,83 +756,7 @@ extension PresentedMotion {
}
}
extension PresentedMotion {
/// Adds the available transition animations.
fileprivate func addTransitionAnimations() {
for (from, to) in transitionPairs {
var snapshotAnimations = [CABasicAnimation]()
var snapshotChildAnimations = [CABasicAnimation]()
let sizeAnimation = Motion.size(to.bounds.size)
let cornerRadiusAnimation = Motion.corner(radius: to.layer.cornerRadius)
snapshotAnimations.append(sizeAnimation)
snapshotAnimations.append(cornerRadiusAnimation)
snapshotAnimations.append(Motion.position(to: to.motionPosition))
snapshotAnimations.append(Motion.transform(transform: to.motionTransform))
snapshotAnimations.append(Motion.background(color: to.backgroundColor ?? .clear))
if let path = to.layer.shadowPath {
let shadowPath = Motion.shadow(path: path)
shadowPath.fromValue = fromView.layer.shadowPath
snapshotAnimations.append(shadowPath)
}
let shadowOffset = Motion.shadow(offset: to.layer.shadowOffset)
shadowOffset.fromValue = fromView.layer.shadowOffset
snapshotAnimations.append(shadowOffset)
let shadowOpacity = Motion.shadow(opacity: to.layer.shadowOpacity)
shadowOpacity.fromValue = fromView.layer.shadowOpacity
snapshotAnimations.append(shadowOpacity)
let shadowRadius = Motion.shadow(radius: to.layer.shadowRadius)
shadowRadius.fromValue = fromView.layer.shadowRadius
snapshotAnimations.append(shadowRadius)
snapshotChildAnimations.append(cornerRadiusAnimation)
snapshotChildAnimations.append(sizeAnimation)
snapshotChildAnimations.append(Motion.position(x: to.bounds.width / 2, y: to.bounds.height / 2))
let d = calculateAnimationTransitionDuration(animations: to.motionAnimations)
let snapshot = from.transitionSnapshot(afterUpdates: true)
transitionView.addSubview(snapshot)
Motion.delay(calculateAnimationDelayTimeInterval(animations: to.motionAnimations)) { [weak self, weak to] in
guard let s = self else {
return
}
guard let v = to else {
return
}
let tf = s.calculateAnimationTimingFunction(animations: v.motionAnimations)
let snapshotGroup = Motion.animate(group: snapshotAnimations, duration: d)
snapshotGroup.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
snapshotGroup.isRemovedOnCompletion = false
snapshotGroup.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: tf)
let snapshotChildGroup = Motion.animate(group: snapshotChildAnimations, duration: d)
snapshotChildGroup.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
snapshotChildGroup.isRemovedOnCompletion = false
snapshotChildGroup.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: tf)
snapshot.animate(snapshotGroup)
snapshot.subviews.first?.animate(snapshotChildGroup)
}
}
}
/// Adds the background animation.
fileprivate func addBackgroundAnimation() {
transitionBackgroundView.motion(.backgroundColor(toView.backgroundColor ?? .clear), .duration(transitionDuration(using: transitionContext)))
}
}
extension PresentedMotion {
extension Motion {
/**
Creates a CAAnimationGroup.
- Parameter animations: An Array of CAAnimation objects.
......@@ -1066,66 +775,7 @@ extension PresentedMotion {
}
}
extension PresentedMotion {
/**
Calculates the animation delay time based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
- Returns: A TimeInterval.
*/
fileprivate func calculateAnimationDelayTimeInterval(animations: [MotionAnimation]) -> TimeInterval {
var t: TimeInterval = 0
for a in animations {
switch a {
case let .delay(time):
if time > delay {
delay = time
}
t = time
default:break
}
}
return t
}
/**
Calculates the animation transition duration based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
- Returns: A TimeInterval.
*/
fileprivate func calculateAnimationTransitionDuration(animations: [MotionAnimation]) -> TimeInterval {
var t: TimeInterval = 0.35
for a in animations {
switch a {
case let .duration(time):
if time > duration {
duration = time
}
t = time
default:break
}
}
return t
}
/**
Calculates the animation timing function based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
- Returns: A MotionAnimationTimingFunction.
*/
fileprivate func calculateAnimationTimingFunction(animations: [MotionAnimation]) -> MotionAnimationTimingFunction {
var t = MotionAnimationTimingFunction.easeInEaseOut
for a in animations {
switch a {
case let .timingFunction(timingFunction):
t = timingFunction
default:break
}
}
return t
}
}
extension PresentedMotion {
extension Motion {
/// Cleans up the animation transition.
fileprivate func cleanUpAnimation() {
Motion.delay(transitionDuration(using: transitionContext) + delayTransitionByTimeInterval) { [weak self] in
......@@ -1167,11 +817,13 @@ extension PresentedMotion {
/// Calls the completionTransition method.
fileprivate func completeTransition() {
// toViewController.motionDelegate?.motion?(motion: self, didTransition: fromView, toView: toView)
toViewController.motionDelegate?.motion?(motion: self, didTransition: fromView, toView: toView)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
open class PresentedMotion: Motion {}
open class DismissedMotion: NSObject {
/**
An Array of UIView pairs with common motionIdentifiers in
......@@ -1314,13 +966,10 @@ extension DismissedMotion {
/// Prepares the toView.
fileprivate func prepareToView() {
toView.isHidden = true
// containerView.insertSubview(toView, belowSubview: transitionView)
//
// toView.frame = fromView.frame
// toView.updateConstraints()
// toView.setNeedsLayout()
// toView.layoutIfNeeded()
toView.frame = fromView.frame
toView.updateConstraints()
toView.setNeedsLayout()
toView.layoutIfNeeded()
}
/// Prepares the transition animation.
......@@ -1410,25 +1059,6 @@ extension DismissedMotion {
extension DismissedMotion {
/**
Creates a CAAnimationGroup.
- Parameter animations: An Array of CAAnimation objects.
- Parameter timingFunction: An MotionAnimationTimingFunction value.
- 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 {
let group = CAAnimationGroup()
group.fillMode = MotionAnimationFillModeToValue(mode: .forwards)
group.isRemovedOnCompletion = false
group.animations = animations
group.duration = duration
group.timingFunction = MotionAnimationTimingFunctionToValue(timingFunction: timingFunction)
return group
}
}
extension DismissedMotion {
/**
Calculates the animation delay time based on the given Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
- Returns: A TimeInterval.
......
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