Commit f85a6744 by Daniel Dahan

added Motion+Start and reworked context references

parent 732bcdb5
......@@ -10,6 +10,10 @@
965FE9611FDCC3AF0098BDD0 /* MotionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9601FDCC3AF0098BDD0 /* MotionState.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 */; };
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 */; };
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; };
96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; };
......@@ -83,6 +87,10 @@
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>"; };
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; };
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>"; };
......@@ -147,6 +155,8 @@
96E4094F1F24F7370015A2B5 /* Motion.h */,
96E409501F24F7370015A2B5 /* Motion.swift */,
965FE9601FDCC3AF0098BDD0 /* MotionState.swift */,
965FE9681FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift */,
965FE9661FDD99800098BDD0 /* Motion+Start.swift */,
965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */,
965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */,
96E409511F24F7370015A2B5 /* MotionAnimation.swift */,
......@@ -170,6 +180,7 @@
isa = PBXGroup;
children = (
96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */,
965FE96C1FDDA6400098BDD0 /* BaseMotionAnimator.swift */,
96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */,
96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */,
96E409401F24F7370015A2B5 /* MotionHasInsertOrder.swift */,
......@@ -198,6 +209,7 @@
96E4095D1F24F7370015A2B5 /* Preprocessors */ = {
isa = PBXGroup;
children = (
965FE96A1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift */,
96E4095E1F24F7370015A2B5 /* CascadePreprocessor.swift */,
96E4095F1F24F7370015A2B5 /* DurationPreprocessor.swift */,
96E409601F24F7370015A2B5 /* IgnoreSubviewModifiersPreprocessor.swift */,
......@@ -313,9 +325,11 @@
96E409731F24F7370015A2B5 /* MotionAnimationFillMode.swift in Sources */,
96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */,
96E409831F24F7370015A2B5 /* DurationPreprocessor.swift in Sources */,
965FE96B1FDDA4EA0098BDD0 /* BaseMotionPreprocessor.swift in Sources */,
965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */,
96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */,
96E409681F24F7370015A2B5 /* MotionHasInsertOrder.swift in Sources */,
965FE96D1FDDA6400098BDD0 /* BaseMotionAnimator.swift in Sources */,
96E4096E1F24F7370015A2B5 /* Motion+CG.swift in Sources */,
96E409851F24F7370015A2B5 /* MatchPreprocessor.swift in Sources */,
96E409861F24F7370015A2B5 /* MotionPreprocessor.swift in Sources */,
......@@ -334,10 +348,12 @@
96E4096B1F24F7370015A2B5 /* Motion+Array.swift in Sources */,
96E409721F24F7370015A2B5 /* Motion+UIViewController.swift in Sources */,
96E4097E1F24F7370015A2B5 /* MotionSnapshotType.swift in Sources */,
965FE9691FDDA1F20098BDD0 /* MotionViewOrderStrategy.swift in Sources */,
96E409871F24F7370015A2B5 /* SourcePreprocessor.swift in Sources */,
96E409701F24F7370015A2B5 /* Motion+UIKit.swift in Sources */,
96E4097B1F24F7370015A2B5 /* MotionCoordinateSpace.swift in Sources */,
96E409881F24F7370015A2B5 /* TransitionPreprocessor.swift in Sources */,
965FE9671FDD99800098BDD0 /* Motion+Start.swift in Sources */,
96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */,
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.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 @@
import UIKit
public protocol MotionAnimator: class {
/// A reference to a MotionContext.
weak var context: MotionContext! { get set }
/// A reference to Motion.
weak var motion: Motion! { get set }
/// Cleans the contexts.
func clean()
......
......@@ -30,7 +30,7 @@ import UIKit
internal class MotionAnimatorViewContext {
/// An optional reference to a MotionAnimator.
var animator: MotionAnimator?
var animator: BaseMotionAnimator?
/// A reference to the snapshot UIView.
var snapshot: UIView
......@@ -57,7 +57,7 @@ internal class MotionAnimatorViewContext {
- Parameter snapshot: A UIView.
- Parameter targetState: A MotionTransitionState.
*/
required init(animator: MotionAnimator, snapshot: UIView, targetState: MotionTransitionState) {
required init(animator: BaseMotionAnimator, snapshot: UIView, targetState: MotionTransitionState) {
self.animator = animator
self.snapshot = snapshot
self.targetState = targetState
......
......@@ -28,36 +28,14 @@
import UIKit
internal class MotionTransitionAnimator<T: MotionAnimatorViewContext>: MotionAnimator, MotionHasInsertOrder {
/// A reference to a MotionContext.
weak public var context: MotionContext!
internal class MotionTransitionAnimator<T: MotionAnimatorViewContext>: BaseMotionAnimator, MotionHasInsertOrder {
/// An index of views to their corresponding animator context.
var viewToContexts = [UIView: T]()
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.
func clean() {
override func clean() {
for v in viewToContexts.values {
v.clean()
}
......@@ -72,7 +50,7 @@ extension MotionTransitionAnimator {
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
override func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
guard let state = context[view] else {
return false
}
......@@ -86,7 +64,7 @@ extension MotionTransitionAnimator {
- Parameter toViews: An Array of UIViews.
- Returns: A TimeInterval.
*/
func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
override func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
var duration: TimeInterval = 0
if insertToViewFirst {
......@@ -119,7 +97,7 @@ extension MotionTransitionAnimator {
Moves the view's animation to the given elapsed time.
- Parameter to elapsedTime: A TimeInterval.
*/
func seek(to elapsedTime: TimeInterval) {
override func seek(to elapsedTime: TimeInterval) {
for v in viewToContexts.values {
v.seek(to: elapsedTime)
}
......@@ -132,7 +110,7 @@ extension MotionTransitionAnimator {
- Parameter isReversed: A boolean to reverse the animation
or not.
*/
func resume(at elapsedTime: TimeInterval, isReversed: Bool) -> TimeInterval {
override func resume(at elapsedTime: TimeInterval, isReversed: Bool) -> TimeInterval {
var duration: TimeInterval = 0
for (_, v) in viewToContexts {
......@@ -148,7 +126,7 @@ extension MotionTransitionAnimator {
- Parameter state: A MotionTransitionState.
- 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 {
return
}
......@@ -156,3 +134,21 @@ extension MotionTransitionAnimator {
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 {
} else if let container = view.superview {
let presentingVC = presentingViewController
Motion.shared.transition(from: self, to: next, in: container) { [weak self] (isFinished) in
guard isFinished else {
Motion.shared.transition(from: self, to: next, in: container) { [weak self] (isFinishing) in
guard isFinishing else {
return
}
......
......@@ -32,36 +32,33 @@ extension Motion {
/**
Complete the transition.
- Parameter after: A TimeInterval.
- Parameter isFinished: A Boolean indicating if the transition
- Parameter isFinishing: A Boolean indicating if the transition
has completed.
*/
func complete(after: TimeInterval, isFinished: Bool) {
guard isTransitioning else {
func complete(after: TimeInterval, isFinishing: Bool) {
guard [MotionState.animating, .starting, .notified].contains(state) else {
return
}
if after <= 0.001 {
complete(isFinished: isFinished)
if after <= 1.0 / 120 {
complete(isFinishing: isFinishing)
return
}
let v = (isFinished ? elapsedTime : 1 - elapsedTime) * totalDuration
let totalTime = after / (isFinishing ? max((1 - elapsedTime), 0.01) : max(elapsedTime, 0.01))
self.isFinished = isFinished
currentAnimationDuration = after + v
beginTime = CACurrentMediaTime() - v
progressRunner.start(timePassed: elapsedTime * totalTime, totalTime: totalTime, reverse: !isFinishing)
}
/**
Complete the transition.
- Parameter isFinished: A Boolean indicating if the transition
- Parameter isFinishing: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinished: Bool) {
func complete(isFinishing: Bool) {
if state == .notified {
forceFinishing = isFinished
forceFinishing = isFinishing
}
guard .animating == state || .starting == state else {
......@@ -97,7 +94,7 @@ extension Motion {
context.clean()
if let tv = toView, let fv = fromView {
if isFinished && isPresenting && toOverFullScreen {
if isFinishing && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen view controller.
context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv)
......@@ -107,7 +104,7 @@ extension Motion {
fv.removeFromSuperview()
fv.addSubview(container)
} else if !isFinished && !isPresenting && fromOverFullScreen {
} else if !isFinishing && !isPresenting && fromOverFullScreen {
// Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv)
......@@ -123,13 +120,13 @@ extension Motion {
}
// Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) {
transitionContainer?.addSubview(isFinished ? fv : tv)
if (toOverFullScreen && isFinishing) || (fromOverFullScreen && !isFinishing) {
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.
// bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication.shared.keyWindow?.addSubview(isPresenting ? fv : tv)
......@@ -146,19 +143,19 @@ extension Motion {
transitionContainer?.isUserInteractionEnabled = true
completionCallback?(isFinished)
completionCallback?(isFinishing)
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
if isFinished {
if isFinishing {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
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
}
}
......@@ -29,6 +29,9 @@
import UIKit
public class MotionContext {
/// Inserts the toViews first.
internal var insertToViewFirst = false
/// A reference of motion identifiers to source views.
internal var motionIdentifierToSourceView = [String: UIView]()
......
......@@ -28,9 +28,7 @@
import UIKit
open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
weak public var context: MotionContext!
class MotionPlugin: BaseMotionPreprocessor, MotionAnimator {
/**
Determines whether or not to receive `seekTo` callback on every frame.
......@@ -64,7 +62,7 @@ open class MotionPlugin: NSObject, MotionPreprocessor, MotionAnimator {
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.
......
......@@ -30,7 +30,7 @@ import UIKit
protocol MotionProgressRunnerDelegate: class {
func update(elapsedTime: TimeInterval)
func complete(isFinished: Bool)
func complete(isFinishing: Bool)
}
class MotionProgressRunner {
......@@ -50,13 +50,13 @@ class MotionProgressRunner {
timePassed += isReversed ? -link.duration : link.duration
if isReversed, timePassed <= 1.0 / 120 {
delegate?.complete(isFinished: false)
delegate?.complete(isFinishing: false)
stop()
return
}
if !isReversed, timePassed > duration - 1.0 / 120 {
delegate?.complete(isFinished: true)
delegate?.complete(isFinishing: true)
stop()
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 {
}
}
class CascadePreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
class CascadePreprocessor: BaseMotionPreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: 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: toViews)
}
......
......@@ -28,19 +28,16 @@
import UIKit
class DurationPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
class DurationPreprocessor: BaseMotionPreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: 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
maxDuration = applyOptimizedDurationIfNoDuration(views:fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views:toViews))
maxDuration = applyOptimizedDurationIfNoDuration(views: fromViews)
maxDuration = max(maxDuration, applyOptimizedDurationIfNoDuration(views: toViews))
setDurationForInfiniteDuration(views: fromViews, duration: maxDuration)
setDurationForInfiniteDuration(views: toViews, duration: maxDuration)
}
......
......@@ -28,16 +28,13 @@
import UIKit
class IgnoreSubviewTransitionsPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
class IgnoreSubviewTransitionsPreprocessor: BaseMotionPreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: 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:toViews)
}
......
......@@ -28,16 +28,13 @@
import UIKit
class MatchPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
class MatchPreprocessor: BaseMotionPreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: 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 {
guard let i = tv.motionIdentifier, let fv = context.sourceView(for: i) else { continue }
......
......@@ -29,8 +29,8 @@
import UIKit
public protocol MotionPreprocessor: class {
/// A reference to a MotionContext.
weak var context: MotionContext! { get set }
/// A reference to Motion.
weak var motion: Motion! { get set }
/**
Processes the transitionary views.
......
......@@ -28,16 +28,13 @@
import UIKit
class SourcePreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
class SourcePreprocessor: BaseMotionPreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: 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 {
guard let i = context[fv]?.motionIdentifier, let tv = context.destinationView(for: i) else {
continue
......
......@@ -172,18 +172,13 @@ public enum MotionTransitionType {
}
}
class TransitionPreprocessor: MotionPreprocessor {
/// A reference to a MotionContext.
weak var context: MotionContext!
/// A reference to a Motion.
weak var motion: Motion?
class TransitionPreprocessor: BaseMotionPreprocessor {
/**
An initializer that accepts a given Motion instance.
- Parameter motion: A Motion.
*/
init(motion: Motion) {
super.init()
self.motion = motion
}
......@@ -192,7 +187,7 @@ class TransitionPreprocessor: MotionPreprocessor {
- Parameter fromViews: 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 {
return
}
......@@ -271,7 +266,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)])
case .pull(let direction):
m.insertToViewFirst = true
context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
......@@ -300,7 +295,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)])
case .uncover(let direction):
m.insertToViewFirst = true
context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
......@@ -319,7 +314,7 @@ class TransitionPreprocessor: MotionPreprocessor {
.timingFunction(.deceleration)])
case .pageOut(let direction):
m.insertToViewFirst = true
context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.translate(shift(direction: direction, isAppearing: false)),
.shadow(opacity: 0),
......@@ -346,7 +341,7 @@ class TransitionPreprocessor: MotionPreprocessor {
context[fv]!.append(.preferredDurationMatchesLongest)
case .zoom:
m.insertToViewFirst = true
context.insertToViewFirst = true
context[fv]!.append(contentsOf: [.scale(1.3), .fadeOut])
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