Commit f85a6744 by Daniel Dahan

added Motion+Start and reworked context references

parent 732bcdb5
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
965FE9611FDCC3AF0098BDD0 /* MotionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9601FDCC3AF0098BDD0 /* MotionState.swift */; }; 965FE9611FDCC3AF0098BDD0 /* MotionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9601FDCC3AF0098BDD0 /* MotionState.swift */; };
965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */; }; 965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */; };
965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */; }; 965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */; };
965FE9671FDD99800098BDD0 /* Motion+Start.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9661FDD99800098BDD0 /* Motion+Start.swift */; };
965FE9691FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9681FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift */; };
965FE96B1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE96A1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift */; };
965FE96D1FDDA6400098BDD0 /* BaseMotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE96C1FDDA6400098BDD0 /* BaseMotionAnimator.swift */; };
96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */; }; 96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */; };
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; }; 96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; };
96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; }; 96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; };
...@@ -83,6 +87,10 @@ ...@@ -83,6 +87,10 @@
965FE9601FDCC3AF0098BDD0 /* MotionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionState.swift; sourceTree = "<group>"; }; 965FE9601FDCC3AF0098BDD0 /* MotionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionState.swift; sourceTree = "<group>"; };
965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Motion+Complete.swift"; sourceTree = "<group>"; }; 965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Motion+Complete.swift"; sourceTree = "<group>"; };
965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionProgressRunner.swift; sourceTree = "<group>"; }; 965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionProgressRunner.swift; sourceTree = "<group>"; };
965FE9661FDD99800098BDD0 /* Motion+Start.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Motion+Start.swift"; sourceTree = "<group>"; };
965FE9681FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionViewOrderStrategy.swift; sourceTree = "<group>"; };
965FE96A1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMotionPreprocessor.swift; sourceTree = "<group>"; };
965FE96C1FDDA6400098BDD0 /* BaseMotionAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseMotionAnimator.swift; sourceTree = "<group>"; };
96C98DD11E424AB000B22906 /* Motion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Motion.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 96C98DD11E424AB000B22906 /* Motion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Motion.framework; sourceTree = BUILT_PRODUCTS_DIR; };
96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimator.swift; sourceTree = "<group>"; }; 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimator.swift; sourceTree = "<group>"; };
96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimatorViewContext.swift; sourceTree = "<group>"; }; 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimatorViewContext.swift; sourceTree = "<group>"; };
...@@ -147,6 +155,8 @@ ...@@ -147,6 +155,8 @@
96E4094F1F24F7370015A2B5 /* Motion.h */, 96E4094F1F24F7370015A2B5 /* Motion.h */,
96E409501F24F7370015A2B5 /* Motion.swift */, 96E409501F24F7370015A2B5 /* Motion.swift */,
965FE9601FDCC3AF0098BDD0 /* MotionState.swift */, 965FE9601FDCC3AF0098BDD0 /* MotionState.swift */,
965FE9681FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift */,
965FE9661FDD99800098BDD0 /* Motion+Start.swift */,
965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */, 965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */,
965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */, 965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */,
96E409511F24F7370015A2B5 /* MotionAnimation.swift */, 96E409511F24F7370015A2B5 /* MotionAnimation.swift */,
...@@ -170,6 +180,7 @@ ...@@ -170,6 +180,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */, 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */,
965FE96C1FDDA6400098BDD0 /* BaseMotionAnimator.swift */,
96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */, 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */,
96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */, 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */,
96E409401F24F7370015A2B5 /* MotionHasInsertOrder.swift */, 96E409401F24F7370015A2B5 /* MotionHasInsertOrder.swift */,
...@@ -198,6 +209,7 @@ ...@@ -198,6 +209,7 @@
96E4095D1F24F7370015A2B5 /* Preprocessors */ = { 96E4095D1F24F7370015A2B5 /* Preprocessors */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
965FE96A1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift */,
96E4095E1F24F7370015A2B5 /* CascadePreprocessor.swift */, 96E4095E1F24F7370015A2B5 /* CascadePreprocessor.swift */,
96E4095F1F24F7370015A2B5 /* DurationPreprocessor.swift */, 96E4095F1F24F7370015A2B5 /* DurationPreprocessor.swift */,
96E409601F24F7370015A2B5 /* IgnoreSubviewModifiersPreprocessor.swift */, 96E409601F24F7370015A2B5 /* IgnoreSubviewModifiersPreprocessor.swift */,
...@@ -313,9 +325,11 @@ ...@@ -313,9 +325,11 @@
96E409731F24F7370015A2B5 /* MotionAnimationFillMode.swift in Sources */, 96E409731F24F7370015A2B5 /* MotionAnimationFillMode.swift in Sources */,
96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */, 96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */,
96E409831F24F7370015A2B5 /* DurationPreprocessor.swift in Sources */, 96E409831F24F7370015A2B5 /* DurationPreprocessor.swift in Sources */,
965FE96B1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift in Sources */,
965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */, 965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */,
96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */, 96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */,
96E409681F24F7370015A2B5 /* MotionHasInsertOrder.swift in Sources */, 96E409681F24F7370015A2B5 /* MotionHasInsertOrder.swift in Sources */,
965FE96D1FDDA6400098BDD0 /* BaseMotionAnimator.swift in Sources */,
96E4096E1F24F7370015A2B5 /* Motion+CG.swift in Sources */, 96E4096E1F24F7370015A2B5 /* Motion+CG.swift in Sources */,
96E409851F24F7370015A2B5 /* MatchPreprocessor.swift in Sources */, 96E409851F24F7370015A2B5 /* MatchPreprocessor.swift in Sources */,
96E409861F24F7370015A2B5 /* MotionPreprocessor.swift in Sources */, 96E409861F24F7370015A2B5 /* MotionPreprocessor.swift in Sources */,
...@@ -334,10 +348,12 @@ ...@@ -334,10 +348,12 @@
96E4096B1F24F7370015A2B5 /* Motion+Array.swift in Sources */, 96E4096B1F24F7370015A2B5 /* Motion+Array.swift in Sources */,
96E409721F24F7370015A2B5 /* Motion+UIViewController.swift in Sources */, 96E409721F24F7370015A2B5 /* Motion+UIViewController.swift in Sources */,
96E4097E1F24F7370015A2B5 /* MotionSnapshotType.swift in Sources */, 96E4097E1F24F7370015A2B5 /* MotionSnapshotType.swift in Sources */,
965FE9691FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift in Sources */,
96E409871F24F7370015A2B5 /* SourcePreprocessor.swift in Sources */, 96E409871F24F7370015A2B5 /* SourcePreprocessor.swift in Sources */,
96E409701F24F7370015A2B5 /* Motion+UIKit.swift in Sources */, 96E409701F24F7370015A2B5 /* Motion+UIKit.swift in Sources */,
96E4097B1F24F7370015A2B5 /* MotionCoordinateSpace.swift in Sources */, 96E4097B1F24F7370015A2B5 /* MotionCoordinateSpace.swift in Sources */,
96E409881F24F7370015A2B5 /* TransitionPreprocessor.swift in Sources */, 96E409881F24F7370015A2B5 /* TransitionPreprocessor.swift in Sources */,
965FE9671FDD99800098BDD0 /* Motion+Start.swift in Sources */,
96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */, 96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */,
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */, 96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */,
965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */, 965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */,
......
/*
* 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 BaseMotionAnimator: MotionAnimator {
weak public var motion: Motion!
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion?.context
}
func clean() {}
func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
return false
}
func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
return 0
}
func seek(to elapsedTime: TimeInterval) {}
func resume(at elapsedTime: TimeInterval, isReversed: Bool) -> TimeInterval {
return 0
}
func apply(state: MotionTransitionState, to view: UIView) {}
}
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
import UIKit import UIKit
public protocol MotionAnimator: class { public protocol MotionAnimator: class {
/// A reference to a MotionContext. /// A reference to Motion.
weak var context: MotionContext! { get set } weak var motion: Motion! { get set }
/// Cleans the contexts. /// Cleans the contexts.
func clean() func clean()
......
...@@ -30,7 +30,7 @@ import UIKit ...@@ -30,7 +30,7 @@ import UIKit
internal class MotionAnimatorViewContext { internal class MotionAnimatorViewContext {
/// An optional reference to a MotionAnimator. /// An optional reference to a MotionAnimator.
var animator: MotionAnimator? var animator: BaseMotionAnimator?
/// A reference to the snapshot UIView. /// A reference to the snapshot UIView.
var snapshot: UIView var snapshot: UIView
...@@ -57,7 +57,7 @@ internal class MotionAnimatorViewContext { ...@@ -57,7 +57,7 @@ internal class MotionAnimatorViewContext {
- Parameter snapshot: A UIView. - Parameter snapshot: A UIView.
- Parameter targetState: A MotionTransitionState. - Parameter targetState: A MotionTransitionState.
*/ */
required init(animator: MotionAnimator, snapshot: UIView, targetState: MotionTransitionState) { required init(animator: BaseMotionAnimator, snapshot: UIView, targetState: MotionTransitionState) {
self.animator = animator self.animator = animator
self.snapshot = snapshot self.snapshot = snapshot
self.targetState = targetState self.targetState = targetState
......
...@@ -28,36 +28,14 @@ ...@@ -28,36 +28,14 @@
import UIKit import UIKit
internal class MotionTransitionAnimator<T: MotionAnimatorViewContext>: MotionAnimator, MotionHasInsertOrder { internal class MotionTransitionAnimator<T: MotionAnimatorViewContext>: BaseMotionAnimator, MotionHasInsertOrder {
/// A reference to a MotionContext.
weak public var context: MotionContext!
/// An index of views to their corresponding animator context. /// An index of views to their corresponding animator context.
var viewToContexts = [UIView: T]() var viewToContexts = [UIView: T]()
var insertToViewFirst = false var insertToViewFirst = false
}
extension MotionTransitionAnimator {
/**
Animates a given view.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
fileprivate func animate(view: UIView, isAppearing: Bool) {
let s = context.snapshotView(for: view)
let v = T(animator: self, snapshot: s, targetState: context[view]!)
viewToContexts[view] = v
v.startAnimations(isAppearing: isAppearing)
}
}
extension MotionTransitionAnimator {
/// Cleans the contexts. /// Cleans the contexts.
func clean() { override func clean() {
for v in viewToContexts.values { for v in viewToContexts.values {
v.clean() v.clean()
} }
...@@ -72,7 +50,7 @@ extension MotionTransitionAnimator { ...@@ -72,7 +50,7 @@ extension MotionTransitionAnimator {
- Parameter isAppearing: A boolean that determines whether the - Parameter isAppearing: A boolean that determines whether the
view is appearing. view is appearing.
*/ */
func canAnimate(view: UIView, isAppearing: Bool) -> Bool { override func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
guard let state = context[view] else { guard let state = context[view] else {
return false return false
} }
...@@ -86,7 +64,7 @@ extension MotionTransitionAnimator { ...@@ -86,7 +64,7 @@ extension MotionTransitionAnimator {
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
- Returns: A TimeInterval. - Returns: A TimeInterval.
*/ */
func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval { override func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
var duration: TimeInterval = 0 var duration: TimeInterval = 0
if insertToViewFirst { if insertToViewFirst {
...@@ -119,7 +97,7 @@ extension MotionTransitionAnimator { ...@@ -119,7 +97,7 @@ extension MotionTransitionAnimator {
Moves the view's animation to the given elapsed time. Moves the view's animation to the given elapsed time.
- Parameter to elapsedTime: A TimeInterval. - Parameter to elapsedTime: A TimeInterval.
*/ */
func seek(to elapsedTime: TimeInterval) { override func seek(to elapsedTime: TimeInterval) {
for v in viewToContexts.values { for v in viewToContexts.values {
v.seek(to: elapsedTime) v.seek(to: elapsedTime)
} }
...@@ -132,7 +110,7 @@ extension MotionTransitionAnimator { ...@@ -132,7 +110,7 @@ extension MotionTransitionAnimator {
- Parameter isReversed: A boolean to reverse the animation - Parameter isReversed: A boolean to reverse the animation
or not. or not.
*/ */
func resume(at elapsedTime: TimeInterval, isReversed: Bool) -> TimeInterval { override func resume(at elapsedTime: TimeInterval, isReversed: Bool) -> TimeInterval {
var duration: TimeInterval = 0 var duration: TimeInterval = 0
for (_, v) in viewToContexts { for (_, v) in viewToContexts {
...@@ -148,7 +126,7 @@ extension MotionTransitionAnimator { ...@@ -148,7 +126,7 @@ extension MotionTransitionAnimator {
- Parameter state: A MotionTransitionState. - Parameter state: A MotionTransitionState.
- Parameter to view: A UIView. - Parameter to view: A UIView.
*/ */
func apply(state: MotionTransitionState, to view: UIView) { override func apply(state: MotionTransitionState, to view: UIView) {
guard let v = viewToContexts[view] else { guard let v = viewToContexts[view] else {
return return
} }
...@@ -156,3 +134,21 @@ extension MotionTransitionAnimator { ...@@ -156,3 +134,21 @@ extension MotionTransitionAnimator {
v.apply(state: state) v.apply(state: state)
} }
} }
extension MotionTransitionAnimator {
/**
Animates a given view.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
fileprivate func animate(view: UIView, isAppearing: Bool) {
let s = context.snapshotView(for: view)
let v = T(animator: self, snapshot: s, targetState: context[view]!)
viewToContexts[view] = v
v.startAnimations(isAppearing: isAppearing)
}
}
...@@ -313,8 +313,8 @@ extension UIViewController { ...@@ -313,8 +313,8 @@ extension UIViewController {
} else if let container = view.superview { } else if let container = view.superview {
let presentingVC = presentingViewController let presentingVC = presentingViewController
Motion.shared.transition(from: self, to: next, in: container) { [weak self] (isFinished) in Motion.shared.transition(from: self, to: next, in: container) { [weak self] (isFinishing) in
guard isFinished else { guard isFinishing else {
return return
} }
......
...@@ -32,36 +32,33 @@ extension Motion { ...@@ -32,36 +32,33 @@ extension Motion {
/** /**
Complete the transition. Complete the transition.
- Parameter after: A TimeInterval. - Parameter after: A TimeInterval.
- Parameter isFinished: A Boolean indicating if the transition - Parameter isFinishing: A Boolean indicating if the transition
has completed. has completed.
*/ */
func complete(after: TimeInterval, isFinished: Bool) { func complete(after: TimeInterval, isFinishing: Bool) {
guard isTransitioning else { guard [MotionState.animating, .starting, .notified].contains(state) else {
return return
} }
if after <= 0.001 { if after <= 1.0 / 120 {
complete(isFinished: isFinished) complete(isFinishing: isFinishing)
return return
} }
let v = (isFinished ? elapsedTime : 1 - elapsedTime) * totalDuration let totalTime = after / (isFinishing ? max((1 - elapsedTime), 0.01) : max(elapsedTime, 0.01))
self.isFinished = isFinished progressRunner.start(timePassed: elapsedTime * totalTime, totalTime: totalTime, reverse: !isFinishing)
currentAnimationDuration = after + v
beginTime = CACurrentMediaTime() - v
} }
/** /**
Complete the transition. Complete the transition.
- Parameter isFinished: A Boolean indicating if the transition - Parameter isFinishing: A Boolean indicating if the transition
has completed. has completed.
*/ */
@objc @objc
func complete(isFinished: Bool) { func complete(isFinishing: Bool) {
if state == .notified { if state == .notified {
forceFinishing = isFinished forceFinishing = isFinishing
} }
guard .animating == state || .starting == state else { guard .animating == state || .starting == state else {
...@@ -97,7 +94,7 @@ extension Motion { ...@@ -97,7 +94,7 @@ extension Motion {
context.clean() context.clean()
if let tv = toView, let fv = fromView { if let tv = toView, let fv = fromView {
if isFinished && isPresenting && toOverFullScreen { if isFinishing && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen view controller. // finished presenting a overFullScreen view controller.
context.unhide(rootView: tv) context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv) context.removeSnapshots(rootView: tv)
...@@ -107,7 +104,7 @@ extension Motion { ...@@ -107,7 +104,7 @@ extension Motion {
fv.removeFromSuperview() fv.removeFromSuperview()
fv.addSubview(container) fv.addSubview(container)
} else if !isFinished && !isPresenting && fromOverFullScreen { } else if !isFinishing && !isPresenting && fromOverFullScreen {
// Cancelled dismissing a overFullScreen view controller. // Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv) context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv) context.removeSnapshots(rootView: fv)
...@@ -123,13 +120,13 @@ extension Motion { ...@@ -123,13 +120,13 @@ extension Motion {
} }
// Move fromView & toView back from our container back to the one supplied by UIKit. // Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) { if (toOverFullScreen && isFinishing) || (fromOverFullScreen && !isFinishing) {
transitionContainer?.addSubview(isFinished ? fv : tv) transitionContainer?.addSubview(isFinishing ? fv : tv)
} }
transitionContainer?.addSubview(isFinished ? tv : fv) transitionContainer?.addSubview(isFinishing ? tv : fv)
if isPresenting != isFinished, !isContainerController { if isPresenting != isFinishing, !isContainerController {
// Only happens when present a .overFullScreen view controller. // Only happens when present a .overFullScreen view controller.
// bug: http://openradar.appspot.com/radar?id=5320103646199808 // bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication.shared.keyWindow?.addSubview(isPresenting ? fv : tv) UIApplication.shared.keyWindow?.addSubview(isPresenting ? fv : tv)
...@@ -146,19 +143,19 @@ extension Motion { ...@@ -146,19 +143,19 @@ extension Motion {
transitionContainer?.isUserInteractionEnabled = true transitionContainer?.isUserInteractionEnabled = true
completionCallback?(isFinished) completionCallback?(isFinishing)
let tContext = transitionContext let tContext = transitionContext
let fvc = fromViewController let fvc = fromViewController
let tvc = toViewController let tvc = toViewController
if isFinished { if isFinishing {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc) processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else { } else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc) processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
tContext?.cancelInteractiveTransition() tContext?.cancelInteractiveTransition()
} }
tContext?.completeTransition(isFinished) tContext?.completeTransition(isFinishing)
} }
} }
/*
* 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
extension Motion {
/// Starts the transition animation.
func start() {
guard .notified == state else {
return
}
state = .starting
prepareToView()
prepareViewControllers()
prepareSnapshotView()
preparePreprocessors()
prepareAnimators()
preparePlugins()
prepareTransitionContainer()
prepareContainer()
prepareContext()
prepareViewHierarchy()
prepareTransitionPairs()
processPreprocessors()
processAnimation()
}
}
fileprivate extension Motion {
/// Prepares the toView instance.
func prepareToView() {
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
if let toViewController = toViewController, let transitionContext = transitionContext {
tv.frame = transitionContext.finalFrame(for: toViewController)
} else {
tv.frame = fv.frame
}
tv.setNeedsLayout()
tv.layoutIfNeeded()
}
/// Prepares the from and to view controllers.
func prepareViewControllers() {
processStartTransitionDelegation(fromViewController: fromViewController, toViewController: toViewController)
}
/// Prepares the snapshot view, which hides any flashing that may occur.
func prepareSnapshotView() {
guard let v = transitionContainer else {
return
}
fullScreenSnapshot = v.window?.snapshotView(afterScreenUpdates: false) ?? fromView?.snapshotView(afterScreenUpdates: false)
(v.window ?? transitionContainer)?.addSubview(fullScreenSnapshot)
if let v = fromViewController?.motionStoredSnapshot {
v.removeFromSuperview()
fromViewController?.motionStoredSnapshot = nil
}
if let v = toViewController?.motionStoredSnapshot {
v.removeFromSuperview()
toViewController?.motionStoredSnapshot = nil
}
}
/// Prepares the preprocessors.
func preparePreprocessors() {
for x in [
IgnoreSubviewTransitionsPreprocessor(),
MatchPreprocessor(),
SourcePreprocessor(),
CascadePreprocessor(),
TransitionPreprocessor(motion: self),
DurationPreprocessor()] as [MotionPreprocessor] {
preprocessors.append(x)
}
for v in preprocessors {
v.motion = self
}
}
/// Prepares the animators.
func prepareAnimators() {
animators.append(MotionTransitionAnimator<MotionCoreAnimationViewContext>())
if #available(iOS 10, tvOS 10, *) {
animators.append(MotionTransitionAnimator<MotionViewPropertyViewContext>())
}
for v in animators {
v.motion = self
}
}
/// Prepares the plugins.
func preparePlugins() {
for x in Motion.enabledPlugins.map({
return $0.init()
}) {
plugins.append(x)
}
for plugin in plugins {
preprocessors.append(plugin)
animators.append(plugin)
}
}
/// Prepares the transition container.
func prepareTransitionContainer() {
guard let v = transitionContainer else {
return
}
v.isUserInteractionEnabled = false
// a view to hold all the animating views
container = UIView(frame: v.bounds)
v.addSubview(container!)
}
/// Prepares the view that holds all the animating views.
func prepareContainer() {
container = UIView(frame: transitionContainer?.bounds ?? .zero)
if !toOverFullScreen && !fromOverFullScreen {
updateContainerBackgroundColor()
}
transitionContainer?.addSubview(container)
}
/// Prepares the MotionContext instance.
func prepareContext() {
context = MotionContext(container: container)
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
context.loadViewAlpha(rootView: tv)
container.addSubview(tv)
context.loadViewAlpha(rootView: fv)
container.addSubview(fv)
tv.setNeedsUpdateConstraints()
tv.updateConstraintsIfNeeded()
tv.setNeedsLayout()
tv.layoutIfNeeded()
}
/// Prepares the view hierarchy.
func prepareViewHierarchy() {
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
context.set(fromViews: fv.flattenedViewHierarchy, toViews: tv.flattenedViewHierarchy)
if (viewOrderStrategy == .auto &&
!isPresenting &&
!isTabBarController) ||
viewOrderStrategy == .sourceViewOnTop {
context.insertToViewFirst = true
}
}
/// Prepares the transition fromView & toView pairs.
@objc
func prepareTransitionPairs() {
guard isTransitioning else {
return
}
for a in animators {
let fv = context.fromViews.filter { (view) -> Bool in
return a.canAnimate(view: view, isAppearing: false)
}
let tv = context.toViews.filter {
return a.canAnimate(view: $0, isAppearing: true)
}
transitionPairs.append((fv, tv))
}
guard let tv = toView else {
return
}
context.hide(view: tv)
}
}
fileprivate extension Motion {
/// Executes the preprocessors' process function.
func processPreprocessors() {
for v in preprocessors {
v.process(fromViews: context.fromViews, toViews: context.toViews)
}
}
/// Processes the animations.
func processAnimation() {
#if os(tvOS)
animate()
#else
if isNavigationController {
// When animating within navigationController, we have to dispatch later into the main queue.
// otherwise snapshots will be pure white. Possibly a bug with UIKit
Motion.async { [weak self] in
self?.animate()
}
} else {
animate()
}
#endif
}
}
...@@ -131,6 +131,13 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -131,6 +131,13 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
/// Shared singleton object for controlling the transition /// Shared singleton object for controlling the transition
public static let shared = Motion() public static let shared = Motion()
/// A boolean indicating if the user may interact with the
/// view controller while in transition.
public var isUserInteractionEnabled = false
/// A reference to the MotionViewOrderStrategy.
public var viewOrderStrategy = MotionViewOrderStrategy.auto
/// Plugins that are enabled during the transition. /// Plugins that are enabled during the transition.
internal static var enabledPlugins = [MotionPlugin.Type]() internal static var enabledPlugins = [MotionPlugin.Type]()
...@@ -142,7 +149,7 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -142,7 +149,7 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
/// A boolean indicating whether the transition interactive or not. /// A boolean indicating whether the transition interactive or not.
public var isInteractive: Bool { public var isInteractive: Bool {
return nil == displayLink return !progressRunner.isRunning
} }
/// Source view controller. /// Source view controller.
...@@ -207,9 +214,15 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -207,9 +214,15 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
/// A boolean indicating whether a transition is active. /// A boolean indicating whether a transition is active.
public var isTransitioning: Bool { public var isTransitioning: Bool {
return nil != transitionContainer return state != .possible
} }
/// Whether or not we are presenting the destination view controller.
public internal(set) var isPresenting = true
/// Indicates whether the transition is animating or not.
public internal(set) var isAnimating = false
/** /**
A view container used to hold all the animating views during a A view container used to hold all the animating views during a
transition. transition.
...@@ -225,9 +238,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -225,9 +238,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
/// An optional completion callback. /// An optional completion callback.
internal var completionCallback: ((Bool) -> Void)? internal var completionCallback: ((Bool) -> Void)?
/// Binds the render cycle to the transition animation.
internal var displayLink: CADisplayLink?
/// An Array of observers that are updated during a transition. /// An Array of observers that are updated during a transition.
internal var transitionObservers: [MotionTransitionObserver]? internal var transitionObservers: [MotionTransitionObserver]?
...@@ -244,27 +254,8 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -244,27 +254,8 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
return runner return runner
}() }()
/// The start time of the animation.
internal var beginTime: TimeInterval? {
didSet {
guard nil != beginTime else {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
return
}
guard nil == displayLink else {
return
}
displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
}
/// A boolean indicating if the transition has finished. /// A boolean indicating if the transition has finished.
internal var isFinished = true internal var isFinishing = true
/// An Array of MotionPreprocessors used during a transition. /// An Array of MotionPreprocessors used during a transition.
internal lazy var preprocessors = [MotionPreprocessor]() internal lazy var preprocessors = [MotionPreprocessor]()
...@@ -278,12 +269,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -278,12 +269,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
/// The matching fromViews to toViews based on the motionIdentifier value. /// The matching fromViews to toViews based on the motionIdentifier value.
internal lazy var transitionPairs = [(fromViews: [UIView], toViews: [UIView])]() internal lazy var transitionPairs = [(fromViews: [UIView], toViews: [UIView])]()
/// Whether or not we are presenting the destination view controller.
public var isPresenting = true
/// Indicates whether the transition is animating or not.
public var isAnimating = false
/// Default animation type. /// Default animation type.
internal var defaultAnimation = MotionTransitionType.auto internal var defaultAnimation = MotionTransitionType.auto
...@@ -296,9 +281,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate { ...@@ -296,9 +281,6 @@ public class Motion: NSObject, MotionProgressRunnerDelegate {
internal var forceFinishing: Bool? internal var forceFinishing: Bool?
internal var startingProgress: CGFloat? internal var startingProgress: CGFloat?
/// Inserts the toViews first.
internal var insertToViewFirst = false
/// Indicates whether a UINavigationController is transitioning. /// Indicates whether a UINavigationController is transitioning.
internal var isNavigationController = false internal var isNavigationController = false
...@@ -378,58 +360,13 @@ private extension Motion { ...@@ -378,58 +360,13 @@ private extension Motion {
} }
} }
private extension Motion {
/**
Handler for the DisplayLink updates.
- Parameter _ link: CADisplayLink.
*/
@objc
func handleDisplayLink(_ link: CADisplayLink) {
guard isTransitioning else {
return
}
guard 0 < currentAnimationDuration else {
return
}
guard let t = beginTime else {
return
}
let cTime = CACurrentMediaTime() - t
if cTime > currentAnimationDuration {
elapsedTime = isFinished ? 1 : 0
beginTime = nil
complete(isFinished: isFinished)
} else {
var eTime = cTime / totalDuration
if !isFinished {
eTime = 1 - eTime
}
elapsedTime = max(0, min(1, eTime))
}
}
}
public extension Motion { public extension Motion {
/** /**
Updates the elapsed time for the interactive transition. Updates the elapsed time for the interactive transition.
- Parameter elapsedTime t: the current progress, must be between -1...1. - Parameter elapsedTime t: the current progress, must be between -1...1.
*/ */
public func update(elapsedTime: TimeInterval) { public func update(elapsedTime: TimeInterval) {
guard isTransitioning else { self.elapsedTime = elapsedTime
return
}
beginTime = nil
self.elapsedTime = max(-1, min(1, elapsedTime))
} }
/** /**
...@@ -444,7 +381,7 @@ public extension Motion { ...@@ -444,7 +381,7 @@ public extension Motion {
} }
guard isAnimated else { guard isAnimated else {
complete(isFinished: true) complete(isFinishing: true)
return return
} }
...@@ -454,7 +391,7 @@ public extension Motion { ...@@ -454,7 +391,7 @@ public extension Motion {
t = max(t, a.resume(at: elapsedTime * totalDuration, isReversed: false)) t = max(t, a.resume(at: elapsedTime * totalDuration, isReversed: false))
} }
complete(after: t, isFinished: true) complete(after: t, isFinishing: true)
} }
/** /**
...@@ -469,7 +406,7 @@ public extension Motion { ...@@ -469,7 +406,7 @@ public extension Motion {
} }
guard isAnimated else { guard isAnimated else {
complete(isFinished: false) complete(isFinishing: false)
return return
} }
...@@ -484,7 +421,7 @@ public extension Motion { ...@@ -484,7 +421,7 @@ public extension Motion {
d = max(d, a.resume(at: t * totalDuration, isReversed: true)) d = max(d, a.resume(at: t * totalDuration, isReversed: true))
} }
complete(after: d, isFinished: false) complete(after: d, isFinishing: false)
} }
/** /**
...@@ -514,64 +451,6 @@ public extension Motion { ...@@ -514,64 +451,6 @@ public extension Motion {
internal extension Motion { internal extension Motion {
/** /**
Load plugins, processors, animators, container, & context
The transitionContainer must already be set.
Subclasses should call context.set(fromViews: toViews) after
inserting fromViews & toViews into the container
*/
@objc
func prepareTransition() {
guard isTransitioning else {
return
}
prepareTransitionContainer()
prepareContext()
preparePreprocessors()
prepareAnimators()
preparePlugins()
}
/// Prepares the transition fromView & toView pairs.
@objc
func prepareTransitionPairs() {
guard isTransitioning else {
return
}
for a in animators {
let fv = context.fromViews.filter { (view) -> Bool in
return a.canAnimate(view: view, isAppearing: false)
}
let tv = context.toViews.filter {
return a.canAnimate(view: $0, isAppearing: true)
}
transitionPairs.append((fv, tv))
}
guard let tv = toView else {
return
}
context.hide(view: tv)
}
}
internal extension Motion {
/// Executes the preprocessors' process function.
func processContext() {
guard isTransitioning else {
return
}
for x in preprocessors {
x.process(fromViews: context.fromViews, toViews: context.toViews)
}
}
/**
Animates the views. Subclasses should call `prepareTransition` & Animates the views. Subclasses should call `prepareTransition` &
`prepareTransitionPairs` before calling `animate`. `prepareTransitionPairs` before calling `animate`.
*/ */
...@@ -615,7 +494,7 @@ internal extension Motion { ...@@ -615,7 +494,7 @@ internal extension Motion {
if b { if b {
update(elapsedTime: 0) update(elapsedTime: 0)
} else { } else {
complete(after: t, isFinished: true) complete(after: t, isFinishing: true)
} }
updateContainerBackgroundColor() updateContainerBackgroundColor()
...@@ -627,66 +506,7 @@ internal extension Motion { ...@@ -627,66 +506,7 @@ internal extension Motion {
} }
} }
private extension Motion { internal extension Motion {
/// Prepares the transition container.
func prepareTransitionContainer() {
guard let v = transitionContainer else {
return
}
v.isUserInteractionEnabled = false
// a view to hold all the animating views
container = UIView(frame: v.bounds)
v.addSubview(container!)
}
/// Prepares the preprocessors.
func preparePreprocessors() {
for x in [
IgnoreSubviewTransitionsPreprocessor(),
MatchPreprocessor(),
SourcePreprocessor(),
CascadePreprocessor(),
TransitionPreprocessor(motion: self),
DurationPreprocessor()] as [MotionPreprocessor] {
preprocessors.append(x)
}
for x in preprocessors {
x.context = context
}
}
/// Prepares the animators.
func prepareAnimators() {
animators.append(MotionTransitionAnimator<MotionCoreAnimationViewContext>())
if #available(iOS 10, tvOS 10, *) {
animators.append(MotionTransitionAnimator<MotionViewPropertyViewContext>())
}
for v in animators {
v.context = context
}
}
/// Prepares the plugins.
func preparePlugins() {
for x in Motion.enabledPlugins.map({
return $0.init()
}) {
plugins.append(x)
}
for plugin in plugins {
preprocessors.append(plugin)
animators.append(plugin)
}
}
}
private extension Motion {
/// Updates the container background color. /// Updates the container background color.
func updateContainerBackgroundColor() { func updateContainerBackgroundColor() {
if let v = containerBackgroundColor { if let v = containerBackgroundColor {
...@@ -700,11 +520,11 @@ private extension Motion { ...@@ -700,11 +520,11 @@ private extension Motion {
/// Updates the insertToViewFirst boolean for animators. /// Updates the insertToViewFirst boolean for animators.
func updateInsertOrder() { func updateInsertOrder() {
if fromOverFullScreen { if fromOverFullScreen {
insertToViewFirst = true context.insertToViewFirst = true
} }
for v in animators { for v in animators {
(v as? MotionHasInsertOrder)?.insertToViewFirst = insertToViewFirst (v as? MotionHasInsertOrder)?.insertToViewFirst = context.insertToViewFirst
} }
} }
} }
...@@ -741,15 +561,6 @@ internal extension Motion { ...@@ -741,15 +561,6 @@ internal extension Motion {
} }
} }
internal extension Motion {
// should call this after `prepareTransitionPairs` & before `processContext`
func insert<T>(preprocessor: MotionPreprocessor, before: T.Type) {
let i = preprocessors.index { $0 is T } ?? preprocessors.count
preprocessor.context = context
preprocessors.insert(preprocessor, at: i)
}
}
public extension Motion { public extension Motion {
/// Turn off built-in animations for the next transition. /// Turn off built-in animations for the next transition.
func disableDefaultAnimationForNextTransition() { func disableDefaultAnimationForNextTransition() {
...@@ -797,145 +608,7 @@ public extension Motion { ...@@ -797,145 +608,7 @@ public extension Motion {
} }
} }
private extension Motion {
/// Starts the transition animation.
func start() {
guard .notified == state else {
return
}
state = .starting
prepareViewControllers()
prepareSnapshotView()
prepareTransition()
prepareContext()
prepareToView()
prepareViewHierarchy()
processContext()
prepareTransitionPairs()
processForAnimation()
}
}
internal extension Motion {
/// Resets the transition values.
func resetTransition() {
transitionContext = nil
fromViewController = nil
toViewController = nil
containerBackgroundColor = nil
isNavigationController = false
isTabBarController = false
forceNonInteractive = false
insertToViewFirst = false
defaultAnimation = .auto
}
}
internal extension Motion {
/// Prepares the from and to view controllers.
func prepareViewControllers() {
processStartTransitionDelegation(fromViewController: fromViewController, toViewController: toViewController)
}
/// Prepares the snapshot view, which hides any flashing that may occur.
func prepareSnapshotView() {
guard let v = transitionContainer else {
return
}
fullScreenSnapshot = v.window?.snapshotView(afterScreenUpdates: true) ?? fromView?.snapshotView(afterScreenUpdates: true)
(v.window ?? transitionContainer)?.addSubview(fullScreenSnapshot)
if let v = fromViewController?.motionStoredSnapshot {
v.removeFromSuperview()
fromViewController?.motionStoredSnapshot = nil
}
if let v = toViewController?.motionStoredSnapshot {
v.removeFromSuperview()
toViewController?.motionStoredSnapshot = nil
}
}
/// Prepares the MotionContext instance.
func prepareContext() {
guard let v = container else {
return
}
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
context = MotionContext(container: v)
context.loadViewAlpha(rootView: tv)
v.addSubview(tv)
context.loadViewAlpha(rootView: fv)
v.addSubview(fv)
}
/// Prepares the toView instance.
func prepareToView() {
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
if let toViewController = toViewController, let transitionContext = transitionContext {
tv.frame = transitionContext.finalFrame(for: toViewController)
} else {
tv.frame = fv.frame
}
tv.setNeedsLayout()
tv.layoutIfNeeded()
}
/// Prepares the view hierarchy.
func prepareViewHierarchy() {
guard let fv = fromView else {
return
}
guard let tv = toView else {
return
}
context.set(fromViews: fv.flattenedViewHierarchy, toViews: tv.flattenedViewHierarchy)
}
}
internal extension Motion { internal extension Motion {
/// Processes the animations.
func processForAnimation() {
#if os(tvOS)
animate()
#else
if isNavigationController {
// When animating within navigationController, we have to dispatch later into the main queue.
// otherwise snapshots will be pure white. Possibly a bug with UIKit
Motion.async { [weak self] in
self?.animate()
}
} else {
animate()
}
#endif
}
/** /**
Processes the start transition delegation methods. Processes the start transition delegation methods.
- Parameter fromViewController: An optional UIViewController. - Parameter fromViewController: An optional UIViewController.
......
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
import UIKit import UIKit
public class MotionContext { public class MotionContext {
/// Inserts the toViews first.
internal var insertToViewFirst = false
/// A reference of motion identifiers to source views. /// A reference of motion identifiers to source views.
internal var motionIdentifierToSourceView = [String: UIView]() internal var motionIdentifierToSourceView = [String: UIView]()
......
...@@ -28,9 +28,7 @@ ...@@ -28,9 +28,7 @@
import UIKit import UIKit
open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { class MotionPlugin: BaseMotionPreprocessor, MotionAnimator {
weak public var context: MotionContext!
/** /**
Determines whether or not to receive `seekTo` callback on every frame. Determines whether or not to receive `seekTo` callback on every frame.
...@@ -64,7 +62,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator { ...@@ -64,7 +62,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
context[view, "transition1"] = ["parameter1", "parameter2"] context[view, "transition1"] = ["parameter1", "parameter2"]
*/ */
open func process(fromViews: [UIView], toViews: [UIView]) {} open override func process(fromViews: [UIView], toViews: [UIView]) {}
/** /**
- Returns: return true if the plugin can handle animating the view. - Returns: return true if the plugin can handle animating the view.
......
...@@ -30,7 +30,7 @@ import UIKit ...@@ -30,7 +30,7 @@ import UIKit
protocol MotionProgressRunnerDelegate: class { protocol MotionProgressRunnerDelegate: class {
func update(elapsedTime: TimeInterval) func update(elapsedTime: TimeInterval)
func complete(isFinished: Bool) func complete(isFinishing: Bool)
} }
class MotionProgressRunner { class MotionProgressRunner {
...@@ -50,13 +50,13 @@ class MotionProgressRunner { ...@@ -50,13 +50,13 @@ class MotionProgressRunner {
timePassed += isReversed ? -link.duration : link.duration timePassed += isReversed ? -link.duration : link.duration
if isReversed, timePassed <= 1.0 / 120 { if isReversed, timePassed <= 1.0 / 120 {
delegate?.complete(isFinished: false) delegate?.complete(isFinishing: false)
stop() stop()
return return
} }
if !isReversed, timePassed > duration - 1.0 / 120 { if !isReversed, timePassed > duration - 1.0 / 120 {
delegate?.complete(isFinished: true) delegate?.complete(isFinishing: true)
stop() stop()
return return
} }
......
/*
* 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.
*/
@objc(MotionViewOrderStrategy)
public enum MotionViewOrderStrategy: Int {
case auto
case sourceViewOnTop
case destinationViewOnTop
}
/*
* 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 BaseMotionPreprocessor: MotionPreprocessor {
weak public var motion: Motion!
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion?.context
}
func process(fromViews: [UIView], toViews: [UIView]) {}
}
...@@ -60,16 +60,13 @@ public enum CascadeDirection { ...@@ -60,16 +60,13 @@ public enum CascadeDirection {
} }
} }
class CascadePreprocessor: MotionPreprocessor { class CascadePreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/** /**
Processes the transitionary views. Processes the transitionary views.
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews) process(views: fromViews)
process(views: toViews) process(views: toViews)
} }
......
...@@ -28,19 +28,16 @@ ...@@ -28,19 +28,16 @@
import UIKit import UIKit
class DurationPreprocessor: MotionPreprocessor { class DurationPreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/** /**
Processes the transitionary views. Processes the transitionary views.
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
var maxDuration: TimeInterval = 0 var maxDuration: TimeInterval = 0
maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews) maxDuration = applyOptimizedDurationIfNoDuration(views: fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews)) maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views: toViews))
setDurationForInfiniteDuration(views: fromViews, duration: maxDuration) setDurationForInfiniteDuration(views: fromViews, duration: maxDuration)
setDurationForInfiniteDuration(views: toViews, duration: maxDuration) setDurationForInfiniteDuration(views: toViews, duration: maxDuration)
} }
......
...@@ -28,16 +28,13 @@ ...@@ -28,16 +28,13 @@
import UIKit import UIKit
class IgnoreSubviewTransitionsPreprocessor: MotionPreprocessor { class IgnoreSubviewTransitionsPreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/** /**
Processes the transitionary views. Processes the transitionary views.
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
process(views:fromViews) process(views:fromViews)
process(views:toViews) process(views:toViews)
} }
......
...@@ -28,16 +28,13 @@ ...@@ -28,16 +28,13 @@
import UIKit import UIKit
class MatchPreprocessor: MotionPreprocessor { class MatchPreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/** /**
Processes the transitionary views. Processes the transitionary views.
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews { for tv in toViews {
guard let i = tv.motionIdentifier, let fv = context.sourceView(for: i) else { continue } guard let i = tv.motionIdentifier, let fv = context.sourceView(for: i) else { continue }
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
import UIKit import UIKit
public protocol MotionPreprocessor: class { public protocol MotionPreprocessor: class {
/// A reference to a MotionContext. /// A reference to Motion.
weak var context: MotionContext! { get set } weak var motion: Motion! { get set }
/** /**
Processes the transitionary views. Processes the transitionary views.
......
...@@ -28,16 +28,13 @@ ...@@ -28,16 +28,13 @@
import UIKit import UIKit
class SourcePreprocessor: MotionPreprocessor { class SourcePreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/** /**
Processes the transitionary views. Processes the transitionary views.
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
for fv in fromViews { for fv in fromViews {
guard let i = context[fv]?.motionIdentifier, let tv = context.destinationView(for: i) else { guard let i = context[fv]?.motionIdentifier, let tv = context.destinationView(for: i) else {
continue continue
......
...@@ -172,18 +172,13 @@ public enum MotionTransitionType { ...@@ -172,18 +172,13 @@ public enum MotionTransitionType {
} }
} }
class TransitionPreprocessor: MotionPreprocessor { class TransitionPreprocessor: BaseMotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/// A reference to a Motion.
weak var motion: Motion?
/** /**
An initializer that accepts a given Motion instance. An initializer that accepts a given Motion instance.
- Parameter motion: A Motion. - Parameter motion: A Motion.
*/ */
init(motion: Motion) { init(motion: Motion) {
super.init()
self.motion = motion self.motion = motion
} }
...@@ -192,7 +187,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -192,7 +187,7 @@ class TransitionPreprocessor: MotionPreprocessor {
- Parameter fromViews: An Array of UIViews. - Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews. - Parameter toViews: An Array of UIViews.
*/ */
func process(fromViews: [UIView], toViews: [UIView]) { override func process(fromViews: [UIView], toViews: [UIView]) {
guard let m = motion else { guard let m = motion else {
return return
} }
...@@ -271,7 +266,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -271,7 +266,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pull(let direction): case .pull(let direction):
m.insertToViewFirst = true context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
...@@ -300,7 +295,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -300,7 +295,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .uncover(let direction): case .uncover(let direction):
m.insertToViewFirst = true context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
...@@ -319,7 +314,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -319,7 +314,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)]) .timingFunction(.deceleration)])
case .pageOut(let direction): case .pageOut(let direction):
m.insertToViewFirst = true context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)), context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0), .shadow(opacity: 0),
...@@ -346,7 +341,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -346,7 +341,7 @@ class TransitionPreprocessor: MotionPreprocessor {
context[fv]!.append(.preferredDurationMatchesLongest) context[fv]!.append(.preferredDurationMatchesLongest)
case .zoom: case .zoom:
m.insertToViewFirst = true context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.scale(1.3), .fadeOut]) context[fv]!.append(contentsOf: [.scale(1.3), .fadeOut])
context[tv]!.append(contentsOf: [.scale(0.7)]) context[tv]!.append(contentsOf: [.scale(0.7)])
......
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