Commit 9465da01 by Daniel Dahan

added MotionTransition and default processors

parent b205f8d1
......@@ -11,7 +11,7 @@
961409B61E43D17200E7BA99 /* Motion+Obj-C.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961409A91E43CF1B00E7BA99 /* Motion+Obj-C.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961409B81E43D21300E7BA99 /* Motion.h in Headers */ = {isa = PBXBuildFile; fileRef = 96C98DED1E438A5700B22906 /* Motion.h */; settings = {ATTRIBUTES = (Public, ); }; };
964C153D1EDCF6EA00F0869D /* Motion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964C153C1EDCF6EA00F0869D /* Motion.swift */; };
964C15471EDD001A00F0869D /* MotionTransitionAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */; };
964C15471EDD001A00F0869D /* MotionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964C15461EDD001A00F0869D /* MotionTransition.swift */; };
9657A6AC1EDA1601004461DE /* MotionObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6AB1EDA1601004461DE /* MotionObserver.swift */; };
9657A6AE1EDA19D8004461DE /* MotionTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6AD1EDA19D8004461DE /* MotionTransitionAnimator.swift */; };
9657A6B31EDA63FC004461DE /* MotionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9657A6B21EDA63FC004461DE /* MotionContext.swift */; };
......@@ -22,21 +22,25 @@
966C53B31EDD325B00A82A57 /* MotionSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */; };
966C53B51EDD327D00A82A57 /* MotionCascadeDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */; };
966C53B71EDD328F00A82A57 /* MotionCoordinateSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */; };
966C53B91EDD366800A82A57 /* MotionTransitionPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */; };
966C53B91EDD366800A82A57 /* TransitionPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53B81EDD366800A82A57 /* TransitionPreprocessor.swift */; };
966C53BD1EDD396800A82A57 /* MotionAnimationFillMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BC1EDD396800A82A57 /* MotionAnimationFillMode.swift */; };
966C53BF1EDD399400A82A57 /* MotionAnimationTimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966C53BE1EDD399400A82A57 /* MotionAnimationTimingFunction.swift */; };
96AEB5F11EE1FA3C009A3BE0 /* MatchPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB5F01EE1FA3C009A3BE0 /* MatchPreprocessor.swift */; };
96AEB6081EE32A38009A3BE0 /* SourcePreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6071EE32A38009A3BE0 /* SourcePreprocessor.swift */; };
96AEB60A1EE339F6009A3BE0 /* DurationPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB6091EE339F6009A3BE0 /* DurationPreprocessor.swift */; };
96AEB60C1EE34012009A3BE0 /* Motion+CG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96AEB60B1EE34012009A3BE0 /* Motion+CG.swift */; };
96BFC1701E63C3460075DE1F /* MotionController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96C98DE31E4382B100B22906 /* MotionController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96C98DE41E4382B100B22906 /* MotionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C98DE31E4382B100B22906 /* MotionController.swift */; };
96C98DE61E43848500B22906 /* MotionAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C98DE51E43848500B22906 /* MotionAnimation.swift */; };
96C98DEB1E4389BE00B22906 /* MotionAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96C98DE51E43848500B22906 /* MotionAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E846F41EDDA62F0005F32F /* DefaultMotionTransitionPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E846F31EDDA62F0005F32F /* DefaultMotionTransitionPreprocessor.swift */; };
96E846F41EDDA62F0005F32F /* AnimationPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E846F31EDDA62F0005F32F /* AnimationPreprocessor.swift */; };
96E846F61EDDA7F20005F32F /* MotionTransitionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E846F51EDDA7F20005F32F /* MotionTransitionState.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
961409A91E43CF1B00E7BA99 /* Motion+Obj-C.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+Obj-C.swift"; sourceTree = "<group>"; };
964C153C1EDCF6EA00F0869D /* Motion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Motion.swift; sourceTree = "<group>"; };
964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionAnimation.swift; sourceTree = "<group>"; };
964C15461EDD001A00F0869D /* MotionTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransition.swift; sourceTree = "<group>"; };
9657A6AB1EDA1601004461DE /* MotionObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionObserver.swift; sourceTree = "<group>"; };
9657A6AD1EDA19D8004461DE /* MotionTransitionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionAnimator.swift; sourceTree = "<group>"; };
9657A6B21EDA63FC004461DE /* MotionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionContext.swift; sourceTree = "<group>"; };
......@@ -47,16 +51,20 @@
966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionSnapshot.swift; sourceTree = "<group>"; };
966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCascadeDirection.swift; sourceTree = "<group>"; };
966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCoordinateSpace.swift; sourceTree = "<group>"; };
966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionPreprocessor.swift; sourceTree = "<group>"; };
966C53B81EDD366800A82A57 /* TransitionPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionPreprocessor.swift; sourceTree = "<group>"; };
966C53BC1EDD396800A82A57 /* MotionAnimationFillMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationFillMode.swift; sourceTree = "<group>"; };
966C53BE1EDD399400A82A57 /* MotionAnimationTimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationTimingFunction.swift; sourceTree = "<group>"; };
96AEB5F01EE1FA3C009A3BE0 /* MatchPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchPreprocessor.swift; sourceTree = "<group>"; };
96AEB6071EE32A38009A3BE0 /* SourcePreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourcePreprocessor.swift; sourceTree = "<group>"; };
96AEB6091EE339F6009A3BE0 /* DurationPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DurationPreprocessor.swift; sourceTree = "<group>"; };
96AEB60B1EE34012009A3BE0 /* Motion+CG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+CG.swift"; sourceTree = "<group>"; };
96C98DD11E424AB000B22906 /* Motion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Motion.framework; sourceTree = BUILT_PRODUCTS_DIR; };
96C98DDD1E424B9000B22906 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
96C98DE21E43809D00B22906 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
96C98DE31E4382B100B22906 /* MotionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionController.swift; sourceTree = "<group>"; };
96C98DE51E43848500B22906 /* MotionAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimation.swift; sourceTree = "<group>"; };
96C98DED1E438A5700B22906 /* Motion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Motion.h; sourceTree = "<group>"; };
96E846F31EDDA62F0005F32F /* DefaultMotionTransitionPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultMotionTransitionPreprocessor.swift; sourceTree = "<group>"; };
96E846F31EDDA62F0005F32F /* AnimationPreprocessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationPreprocessor.swift; sourceTree = "<group>"; };
96E846F51EDDA7F20005F32F /* MotionTransitionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionState.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
......@@ -99,7 +107,7 @@
9657A6B21EDA63FC004461DE /* MotionContext.swift */,
96C98DE51E43848500B22906 /* MotionAnimation.swift */,
96E846F51EDDA7F20005F32F /* MotionTransitionState.swift */,
964C15461EDD001A00F0869D /* MotionTransitionAnimation.swift */,
964C15461EDD001A00F0869D /* MotionTransition.swift */,
966C53B21EDD325B00A82A57 /* MotionSnapshot.swift */,
966C53B41EDD327D00A82A57 /* MotionCascadeDirection.swift */,
966C53B61EDD328F00A82A57 /* MotionCoordinateSpace.swift */,
......@@ -115,8 +123,11 @@
96E846EA1EDD4FB30005F32F /* Preprocessor */ = {
isa = PBXGroup;
children = (
966C53B81EDD366800A82A57 /* MotionTransitionPreprocessor.swift */,
96E846F31EDDA62F0005F32F /* DefaultMotionTransitionPreprocessor.swift */,
966C53B81EDD366800A82A57 /* TransitionPreprocessor.swift */,
96E846F31EDDA62F0005F32F /* AnimationPreprocessor.swift */,
96AEB5F01EE1FA3C009A3BE0 /* MatchPreprocessor.swift */,
96AEB6071EE32A38009A3BE0 /* SourcePreprocessor.swift */,
96AEB6091EE339F6009A3BE0 /* DurationPreprocessor.swift */,
);
name = Preprocessor;
sourceTree = "<group>";
......@@ -137,6 +148,7 @@
966C539F1EDD20DA00A82A57 /* Motion+UIViewController.swift */,
966C53AE1EDD2F8B00A82A57 /* Motion+CALayer.swift */,
966C53B01EDD2FE600A82A57 /* Motion+CABasicAnimation.swift */,
96AEB60B1EE34012009A3BE0 /* Motion+CG.swift */,
);
name = Extension;
sourceTree = "<group>";
......@@ -225,23 +237,27 @@
buildActionMask = 2147483647;
files = (
966C53BD1EDD396800A82A57 /* MotionAnimationFillMode.swift in Sources */,
966C53B91EDD366800A82A57 /* MotionTransitionPreprocessor.swift in Sources */,
96E846F41EDDA62F0005F32F /* DefaultMotionTransitionPreprocessor.swift in Sources */,
966C53B91EDD366800A82A57 /* TransitionPreprocessor.swift in Sources */,
96E846F41EDDA62F0005F32F /* AnimationPreprocessor.swift in Sources */,
966C53B71EDD328F00A82A57 /* MotionCoordinateSpace.swift in Sources */,
966C539E1EDD207800A82A57 /* Motion+UIView.swift in Sources */,
96AEB5F11EE1FA3C009A3BE0 /* MatchPreprocessor.swift in Sources */,
966C53B31EDD325B00A82A57 /* MotionSnapshot.swift in Sources */,
96C98DE61E43848500B22906 /* MotionAnimation.swift in Sources */,
96AEB60A1EE339F6009A3BE0 /* DurationPreprocessor.swift in Sources */,
9657A6AE1EDA19D8004461DE /* MotionTransitionAnimator.swift in Sources */,
9657A6AC1EDA1601004461DE /* MotionObserver.swift in Sources */,
9657A6B31EDA63FC004461DE /* MotionContext.swift in Sources */,
966C53B51EDD327D00A82A57 /* MotionCascadeDirection.swift in Sources */,
964C15471EDD001A00F0869D /* MotionTransitionAnimation.swift in Sources */,
964C15471EDD001A00F0869D /* MotionTransition.swift in Sources */,
96E846F61EDDA7F20005F32F /* MotionTransitionState.swift in Sources */,
966C53B11EDD2FE600A82A57 /* Motion+CABasicAnimation.swift in Sources */,
961409AA1E43CF1B00E7BA99 /* Motion+Obj-C.swift in Sources */,
964C153D1EDCF6EA00F0869D /* Motion.swift in Sources */,
966C53AF1EDD2F8B00A82A57 /* Motion+CALayer.swift in Sources */,
966C53A01EDD20DA00A82A57 /* Motion+UIViewController.swift in Sources */,
96AEB6081EE32A38009A3BE0 /* SourcePreprocessor.swift in Sources */,
96AEB60C1EE34012009A3BE0 /* Motion+CG.swift in Sources */,
96C98DE41E4382B100B22906 /* MotionController.swift in Sources */,
966C53BF1EDD399400A82A57 /* MotionAnimationTimingFunction.swift in Sources */,
);
......
......@@ -30,16 +30,38 @@
import UIKit
public class DefaultMotionTransitionPreprocessor: MotionTransitionPreprocessor {
public class AnimationPreprocessor: TransitionPreprocessor {
/// A reference to the Motion instance.
fileprivate var motion: Motion
/// A reference to a MotionContext.
public weak var context: MotionContext!
/**
Implementation for processors.
Initializer that accepts a Motion instance.
- Parameter motion: A Motion instance.
*/
init(motion: Motion) {
self.motion = motion
}
/**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
public func process(fromViews: [UIView], toViews: [UIView]) {
/*
let inNavigationController = motion.isNavigationController
let inTabBarController = motion.isTabBarController
let toViewController = motion.toViewController
let fromViewController = motion.fromViewController
let presenting = motion.isPresenting
let fromOverFullScreen = motion.isFromViewFullScreen
let toOverFullScreen = motion.isToViewFullScreen
let fromView = motion.fromView
let toView = motion.toView
let animators = motion.animators
*/
}
}
// The MIT License (MIT)
//
// 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
public class DurationPreprocessor: TransitionPreprocessor {
/// A reference to a MotionContext.
public weak var context: MotionContext!
/**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
public func process(fromViews: [UIView], toViews: [UIView]) {
var duration: TimeInterval = 0
duration = applyOptimizedDuration(for: fromViews)
duration = max(duration, applyOptimizedDuration(for: toViews))
set(duration: duration, for: fromViews)
set(duration: duration, for: toViews)
}
}
extension DurationPreprocessor {
fileprivate func set(duration: TimeInterval, for views: [UIView]) {
for view in views where .infinity == context.viewToMotionTransitionState[view]?.duration {
context.viewToMotionTransitionState[view]?.duration = duration
}
}
fileprivate func applyOptimizedDuration(for views: [UIView]) -> TimeInterval {
var duration: TimeInterval = 0
for v in views {
guard var state = context.viewToMotionTransitionState[v] else {
continue
}
if state.duration == nil {
state.duration = optimizedDuration(for: v)
}
if state.duration! == .infinity {
duration = max(duration, optimizedDuration(for: v))
} else {
duration = max(duration, state.duration!)
}
}
return duration
}
fileprivate func optimizedDuration(for view: UIView) -> TimeInterval {
guard let state = context.viewToMotionTransitionState[view] else {
return 0
}
return view.optimizedDuration(fromPosition: context.container.convert(view.layer.position, from: view.superview), toPosition: state.position, size: state.size, transform: state.transform)
}
}
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
public class MatchPreprocessor: TransitionPreprocessor {
/// A reference to a MotionContext.
public weak var context: MotionContext!
/**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
public func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews {
guard let identifier = tv.motionIdentifier else {
return
}
guard let fv = context.sourceIdentifierToView[identifier] else {
continue
}
var tvState = context.viewToMotionTransitionState[tv] ?? MotionTransitionState()
var fvState = context.viewToMotionTransitionState[fv] ?? MotionTransitionState()
if let v = tvState.startStateIfMatched {
tvState.append(.startWith(animations: v))
}
if let v = fvState.startStateIfMatched {
fvState.append(.startWith(animations: v))
}
fvState.motionIdentifier = identifier
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
fvState.delay = tvState.delay
fvState.spring = tvState.spring
tvState.motionIdentifier = identifier
tvState.opacity = 0
if !fv.isOpaque || fv.alpha < 1 || !tv.isOpaque || tv.alpha < 1 {
fvState.opacity = 0
} else {
fvState.opacity = nil
if !fv.layer.masksToBounds && fvState.displayShadow {
tvState.displayShadow = false
}
}
context.viewToMotionTransitionState[tv] = tvState
context.viewToMotionTransitionState[fv] = fvState
}
}
}
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import MetalKit
internal struct KeySet<Key: Hashable, Value: Hashable> {
/// A reference to the dictionary storing the key / Set pairs.
fileprivate var dictionary = [Key: Set<Value>]()
/**
Subscript for matching keys and returning the corresponding set.
- Parameter key: A Key type.
- Returns: A Set<Value> type.
*/
subscript(key: Key) -> Set<Value> {
mutating get {
if nil == dictionary[key] {
dictionary[key] = Set<Value>()
}
return dictionary[key]!
}
set(value) {
dictionary[key] = value
}
}
}
internal extension CGSize {
internal var center: CGPoint {
return CGPoint(x: width / 2, y: height / 2)
}
internal var point: CGPoint {
return CGPoint(x: width, y: height)
}
internal func transform(_ t: CGAffineTransform) -> CGSize {
return applying(t)
}
internal func transform(_ t: CATransform3D) -> CGSize {
return applying(CATransform3DGetAffineTransform(t))
}
}
internal extension CGRect {
var center: CGPoint {
return CGPoint(x: origin.x + size.width / 2, y: origin.y + size.height / 2)
}
var bounds: CGRect {
return CGRect(origin: CGPoint.zero, size: size)
}
init(center: CGPoint, size: CGSize) {
self.init(x: center.x - size.width / 2, y: center.y - size.height / 2, width: size.width, height: size.height)
}
}
internal extension CGFloat {
func clamp(_ a: CGFloat, _ b: CGFloat) -> CGFloat {
return self < a ? a : self > b ? b : self
}
}
internal extension CGPoint {
func translate(_ dx: CGFloat, dy: CGFloat) -> CGPoint {
return CGPoint(x: x + dx, y: y + dy)
}
func transform(_ t: CGAffineTransform) -> CGPoint {
return applying(t)
}
func transform(_ t: CATransform3D) -> CGPoint {
return applying(CATransform3DGetAffineTransform(t))
}
func distance(_ b: CGPoint) -> CGFloat {
return sqrt(pow(x - b.x, 2) + pow(y - b.y, 2))
}
}
internal func +(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
internal func -(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x - right.x, y: left.y - right.y)
}
internal func /(left: CGPoint, right: CGFloat) -> CGPoint {
return CGPoint(x: left.x / right, y: left.y / right)
}
internal func /(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x / right.x, y: left.y / right.y)
}
internal func *(left: CGPoint, right: CGFloat) -> CGPoint {
return CGPoint(x: left.x * right, y: left.y * right)
}
internal func *(left: CGPoint, right: CGSize) -> CGPoint {
return CGPoint(x: left.x * right.width, y: left.y * right.width)
}
internal func *(left: CGFloat, right: CGPoint) -> CGPoint {
return right * left
}
internal func *(left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x * right.x, y: left.y * right.y)
}
internal prefix func -(point: CGPoint) -> CGPoint {
return CGPoint.zero - point
}
internal func abs(_ p: CGPoint) -> CGPoint {
return CGPoint(x: abs(p.x), y: abs(p.y))
}
internal func *(left: CGSize, right: CGFloat) -> CGSize {
return CGSize(width: left.width * right, height: left.height * right)
}
internal func *(left: CGSize, right: CGSize) -> CGSize {
return CGSize(width: left.width * right.width, height: left.height * right.width)
}
internal func /(left: CGSize, right: CGSize) -> CGSize {
return CGSize(width: left.width / right.width, height: left.height / right.height)
}
internal func == (lhs: CATransform3D, rhs: CATransform3D) -> Bool {
var lhs = lhs
var rhs = rhs
return memcmp(&lhs, &rhs, MemoryLayout<CATransform3D>.size) == 0
}
internal func != (lhs: CATransform3D, rhs: CATransform3D) -> Bool {
return !(lhs == rhs)
}
......@@ -41,6 +41,12 @@ fileprivate struct MotionInstance {
/// An optional reference to the motion animations.
fileprivate var animations: [MotionAnimation]?
/// An optional reference to the motion transition animations.
fileprivate var transitions: [MotionTransition]?
/// An alpha value.
fileprivate var alpha: CGFloat?
}
extension UIView {
......@@ -48,7 +54,7 @@ extension UIView {
fileprivate var motionInstance: MotionInstance {
get {
return AssociatedObject.get(base: self, key: &MotionInstanceKey) {
return MotionInstance(isEnabled: true, identifier: nil, animations: nil)
return MotionInstance(isEnabled: true, identifier: nil, animations: nil, transitions: nil, alpha: 1)
}
}
set(value) {
......@@ -78,7 +84,7 @@ extension UIView {
}
}
/// The animations to run while in transition.
/// The animations to run.
open var motionAnimations: [MotionAnimation]? {
get {
return motionInstance.animations
......@@ -87,6 +93,27 @@ extension UIView {
motionInstance.animations = value
}
}
/// The animations to run while in transition.
open var motionTransitions: [MotionTransition]? {
get {
return motionInstance.transitions
}
set(value) {
motionInstance.transitions = value
}
}
/// The animations to run while in transition.
@IBInspectable
open var motionAlpha: CGFloat? {
get {
return motionInstance.alpha
}
set(value) {
motionInstance.alpha = value
}
}
}
extension UIView {
......@@ -168,6 +195,27 @@ extension UIView {
}
extension UIView {
internal func optimizedDuration(fromPosition: CGPoint, toPosition: CGPoint?, size: CGSize?, transform: CATransform3D?) -> TimeInterval {
let toPos = toPosition ?? fromPosition
let fromSize = (layer.presentation() ?? layer).bounds.size
let toSize = size ?? fromSize
let fromTransform = (layer.presentation() ?? layer).transform
let toTransform = transform ?? fromTransform
let realFromPos = CGPoint.zero.transform(fromTransform) + fromPosition
let realToPos = CGPoint.zero.transform(toTransform) + toPos
let realFromSize = fromSize.transform(fromTransform)
let realToSize = toSize.transform(toTransform)
let movePoints = realFromPos.distance(realToPos) + realFromSize.point.distance(realToSize.point)
// duration is 0.2 @ 0 to 0.375 @ 500
return 0.208 + Double(movePoints.clamp(0, 500)) / 3000
}
}
extension UIView {
/// Computes the rotation of the view.
open var motionRotationAngle: CGFloat {
get {
......
......@@ -43,43 +43,45 @@ public class Motion: MotionController {
A boolean indicating if the transition view controller is a
UINavigationController.
*/
fileprivate var isNavigationController = false
internal fileprivate(set) var isNavigationController = false
/**
A boolean indicating if the transition view controller is a
UITabBarController.
*/
fileprivate var isTabBarController = false
internal fileprivate(set) var isTabBarController = false
/**
A boolean indicating if the transition view controller is a
UINavigationController or UITabBarController.
*/
fileprivate var isContainerController: Bool {
internal var isContainerController: Bool {
return isNavigationController || isTabBarController
}
/// A boolean indicating if the toView is at full screen.
fileprivate var isToViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == toViewController!.modalPresentationStyle || .overCurrentContext == toViewController!.modalPresentationStyle)
}
fileprivate var isFromViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == fromViewController!.modalPresentationStyle || .overCurrentContext == fromViewController!.modalPresentationStyle)
}
/// A reference to the fromViewController.view.
fileprivate var fromView: UIView {
internal var fromView: UIView {
return fromViewController!.view
}
/// A reference to the toViewController.view.
fileprivate var toView: UIView {
internal var toView: UIView {
return toViewController!.view
}
/// A reference to the screen snapshot.
fileprivate var screenSnapshot: UIView!
/// A boolean indicating if the fromView is at full screen.
internal var isFromViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == fromViewController!.modalPresentationStyle || .overCurrentContext == fromViewController!.modalPresentationStyle)
}
/// A boolean indicating if the toView is at full screen.
internal var isToViewFullScreen: Bool {
return !isContainerController && (.overFullScreen == toViewController!.modalPresentationStyle || .overCurrentContext == toViewController!.modalPresentationStyle)
}
/**
A reference to a shared Motion instance to control interactive
transitions.
......@@ -212,7 +214,12 @@ extension Motion {
/// Prepares the preprocessors.
fileprivate func preparePreprocessors() {
preprocessors = [DefaultMotionTransitionPreprocessor()]
preprocessors = [
MatchPreprocessor(),
SourcePreprocessor(),
AnimationPreprocessor(motion: self),
DurationPreprocessor(),
]
}
/// Prepares the animators.
......@@ -279,6 +286,11 @@ extension Motion {
processContext()
}
/**
Called when the animation is thought to be completed.
- Parameter isFinished: A boolean value indicatin if the
transition has completed.
*/
fileprivate func completed(isFinished: Bool) {
transitionContext?.completeTransition(!isFinished)
}
......
......@@ -32,16 +32,22 @@ import UIKit
open class MotionContext {
/// A reference to the transition container.
fileprivate var container: UIView
internal fileprivate(set) var container: UIView
/// An index source of identifiers to their corresponding view.
fileprivate var sourceIdentifierToView = [String: UIView]()
internal fileprivate(set) var sourceIdentifierToView = [String: UIView]()
/// An index of destination identifiers to their corresponding view.
fileprivate var destinationIdentifierToView = [String: UIView]()
internal fileprivate(set) var destinationIdentifierToView = [String: UIView]()
/// An index of views to their corresponding snapshot view.
fileprivate var snapshotToView = [UIView: UIView]()
internal fileprivate(set) var snapshotToView = [UIView: UIView]()
/// An index of views to their MotionTransitionState.
internal var viewToMotionTransitionState = [UIView: MotionTransitionState]()
/// An index of views to their alpha value.
internal var viewToAlpha = [UIView: CGFloat]()
/// A reference to the transition from views.
internal var fromViews: [UIView]!
......@@ -83,6 +89,27 @@ extension MotionContext {
extension MotionContext {
/**
Retrieves the transition pair for a given view, if one exists.
- Parameter for view: A UIView.
- Returns: An optional UIView.
*/
internal func transitionPair(for view: UIView) -> UIView? {
guard let identifier = view.motionIdentifier else {
return nil
}
guard let source = sourceIdentifierToView[identifier] else {
return nil
}
guard let destination = destinationIdentifierToView[identifier] else {
return nil
}
return view == source ? destination : view == destination ? source : nil
}
/**
Sets the views that will transition from one state to another.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
......@@ -90,7 +117,114 @@ extension MotionContext {
internal func set(fromViews: [UIView], toViews: [UIView]) {
self.fromViews = fromViews
self.toViews = toViews
prepare(views: fromViews, identifierIndex: &sourceIdentifierToView)
prepare(views: toViews, identifierIndex: &destinationIdentifierToView)
}
internal func hide(view: UIView) {
guard nil == viewToAlpha[view] else {
return
}
guard .none != viewToMotionTransitionState[view]?.motionSnapshot else {
return
}
guard view is UIVisualEffectView else {
view.isHidden = true
viewToAlpha[view] = 1
return
}
viewToAlpha[view] = view.isOpaque ? .infinity : view.alpha
view.alpha = 0
}
internal func show(view: UIView) {
guard let v = viewToAlpha[view] else {
return
}
if view is UIVisualEffectView {
view.isHidden = false
} else if v == .infinity {
view.alpha = 1
view.isOpaque = true
} else {
view.alpha = v
}
viewToAlpha[view] = nil
}
internal func showAll() {
for v in viewToAlpha.keys {
show(view: v)
}
viewToAlpha.removeAll()
}
internal func showSubviews(for view: UIView) {
show(view: view)
for subview in view.subviews {
showSubviews(for: subview)
}
}
internal func removeAllSnapshots() {
for (view, snapshot) in snapshotToView {
guard view != snapshot else {
continue
}
// Do not remove when using .useNoSnapshot.
snapshot.removeFromSuperview()
}
}
internal func removeSnapshots(rootView: UIView) {
if let snapshot = snapshotToView[rootView], snapshot != rootView {
snapshot.removeFromSuperview()
}
for subview in rootView.subviews {
removeSnapshots(rootView: subview)
}
}
internal func snapshots(for view: UIView) -> [UIView] {
var snapshots = [UIView]()
for v in view.flattenedViewHierarchy {
guard let snapshot = snapshotToView[v] else {
continue
}
snapshots.append(snapshot)
}
return snapshots
}
internal func loadViewToAlpha(for view: UIView) {
if let v = view.motionAlpha {
view.alpha = v
view.motionAlpha = nil
}
for subview in view.subviews {
loadViewToAlpha(for: subview)
}
}
internal func storeViewToAlpha(for view: UIView) {
view.motionAlpha = viewToAlpha[view]
for subview in view.subviews {
storeViewToAlpha(for: subview)
}
}
}
......@@ -98,7 +98,7 @@ public class MotionController: NSObject, MotionSubscriber {
public internal(set) var animators = [MotionTransitionAnimator]()
/// A reference to the preprocessors.
public internal(set) var preprocessors = [MotionTransitionPreprocessor]()
public internal(set) var preprocessors = [TransitionPreprocessor]()
/// A boolean indicating if a transition is in progress.
public var isTransitioning: Bool {
......
......@@ -30,7 +30,7 @@
import UIKit
public final class MotionTransitionAnimation {
public final class MotionTransition {
/// A reference to the callback that applies the MotionTransitionState.
internal let apply: (inout MotionTransitionState) -> Void
......@@ -43,14 +43,14 @@ public final class MotionTransitionAnimation {
}
}
extension MotionTransitionAnimation {
extension MotionTransition {
/**
Animates the view with a matching motion identifier.
- Parameter _ identifier: A String.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func motionIdentifier(_ identifier: String) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func motionIdentifier(_ identifier: String) -> MotionTransition {
return MotionTransition {
$0.motionIdentifier = identifier
}
}
......@@ -59,10 +59,10 @@ extension MotionTransitionAnimation {
Animates the view's current background color to the
given color.
- Parameter color: A UIColor.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func background(color: UIColor) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func background(color: UIColor) -> MotionTransition {
return MotionTransition {
$0.backgroundColor = color.cgColor
}
}
......@@ -71,10 +71,10 @@ extension MotionTransitionAnimation {
Animates the view's current border color to the
given color.
- Parameter color: A UIColor.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func border(color: UIColor) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func border(color: UIColor) -> MotionTransition {
return MotionTransition {
$0.borderColor = color.cgColor
}
}
......@@ -83,10 +83,10 @@ extension MotionTransitionAnimation {
Animates the view's current border width to the
given width.
- Parameter width: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func border(width: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func border(width: CGFloat) -> MotionTransition {
return MotionTransition {
$0.borderWidth = width
}
}
......@@ -95,10 +95,10 @@ extension MotionTransitionAnimation {
Animates the view's current corner radius to the
given radius.
- Parameter radius: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func corner(radius: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func corner(radius: CGFloat) -> MotionTransition {
return MotionTransition {
$0.cornerRadius = radius
}
}
......@@ -107,10 +107,10 @@ extension MotionTransitionAnimation {
Animates the view's current transform (perspective, scale, rotation)
to the given one.
- Parameter _ transform: A CATransform3D.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func transform(_ transform: CATransform3D) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func transform(_ transform: CATransform3D) -> MotionTransition {
return MotionTransition {
$0.transform = transform
}
}
......@@ -119,10 +119,10 @@ extension MotionTransitionAnimation {
Animates the view's current perspective to the gievn one through
a CATransform3D object.
- Parameter _ perspective: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func perspective(_ perspective: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func perspective(_ perspective: CGFloat) -> MotionTransition {
return MotionTransition {
var t = $0.transform ?? CATransform3DIdentity
t.m34 = 1 / -perspective
$0.transform = t
......@@ -135,10 +135,10 @@ extension MotionTransitionAnimation {
- Parameter x: A CGFloat.
- Parameter y: A CGFloat.
- Parameter z: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func rotation(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func rotation(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition {
return MotionTransition {
var t = $0.transform ?? CATransform3DIdentity
t = CATransform3DRotate(t, x, 1, 0, 0)
t = CATransform3DRotate(t, y, 0, 1, 0)
......@@ -150,9 +150,9 @@ extension MotionTransitionAnimation {
Animates the view's current rotation to the given point.
- Parameter _ point: A CGPoint.
- Parameter z: A CGFloat, default is 0.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func rotation(_ point: CGPoint, z: CGFloat = 0) -> MotionTransitionAnimation {
public static func rotation(_ point: CGPoint, z: CGFloat = 0) -> MotionTransition {
return .rotation(x: point.x, y: point.y, z: z)
}
......@@ -161,10 +161,10 @@ extension MotionTransitionAnimation {
- Parameter x: A CGFloat.
- Parameter y: A CGFloat.
- Parameter z: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func scale(x: CGFloat = 1, y: CGFloat = 1, z: CGFloat = 1) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func scale(x: CGFloat = 1, y: CGFloat = 1, z: CGFloat = 1) -> MotionTransition {
return MotionTransition {
$0.transform = CATransform3DScale($0.transform ?? CATransform3DIdentity, x, y, z)
}
}
......@@ -172,9 +172,9 @@ extension MotionTransitionAnimation {
/**
Animates the view's current x & y scale to the given scale value.
- Parameter to scale: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func scale(to scale: CGFloat) -> MotionTransitionAnimation {
public static func scale(to scale: CGFloat) -> MotionTransition {
return .scale(x: scale, y: scale)
}
......@@ -184,10 +184,10 @@ extension MotionTransitionAnimation {
- Parameter x: A CGFloat.
- Parameter y: A CGFloat.
- Parameter z: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func translate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func translate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionTransition {
return MotionTransition {
$0.transform = CATransform3DTranslate($0.transform ?? CATransform3DIdentity, x, y, z)
}
}
......@@ -197,40 +197,40 @@ extension MotionTransitionAnimation {
point value (x & y), and a z value.
- Parameter to point: A CGPoint.
- Parameter z: A CGFloat, default is 0.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func translate(to point: CGPoint, z: CGFloat = 0) -> MotionTransitionAnimation {
public static func translate(to point: CGPoint, z: CGFloat = 0) -> MotionTransition {
return .translate(x: point.x, y: point.y, z: z)
}
/**
Animates the view's current position to the given point.
- Parameter to point: A CGPoint.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func position(to point: CGPoint) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func position(to point: CGPoint) -> MotionTransition {
return MotionTransition {
$0.position = point
}
}
/// Fades the view out during a transition.
public static var fadeOut = MotionTransitionAnimation {
public static var fadeOut = MotionTransition {
$0.opacity = 0
}
/// Fades the view in during a transition.
public static var fadeIn = MotionTransitionAnimation {
public static var fadeIn = MotionTransition {
$0.opacity = 1
}
/**
Animates the view's current opacity to the given one.
- Parameter to opacity: A Float value.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func fade(to opacity: Float) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func fade(to opacity: Float) -> MotionTransition {
return MotionTransition {
$0.opacity = opacity
}
}
......@@ -238,10 +238,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current zPosition to the given position.
- Parameter _ position: An Int.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func zPosition(_ position: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func zPosition(_ position: CGFloat) -> MotionTransition {
return MotionTransition {
$0.zPosition = position
}
}
......@@ -249,10 +249,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current size to the given one.
- Parameter _ size: A CGSize.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func size(_ size: CGSize) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func size(_ size: CGSize) -> MotionTransition {
return MotionTransition {
$0.size = size
}
}
......@@ -260,10 +260,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current shadow path to the given one.
- Parameter path: A CGPath.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func shadow(path: CGPath) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func shadow(path: CGPath) -> MotionTransition {
return MotionTransition {
$0.shadowPath = path
}
}
......@@ -271,10 +271,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current shadow color to the given one.
- Parameter color: A UIColor.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func shadow(color: UIColor) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func shadow(color: UIColor) -> MotionTransition {
return MotionTransition {
$0.shadowColor = color.cgColor
}
}
......@@ -282,10 +282,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current shadow offset to the given one.
- Parameter offset: A CGSize.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func shadow(offset: CGSize) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func shadow(offset: CGSize) -> MotionTransition {
return MotionTransition {
$0.shadowOffset = offset
}
}
......@@ -293,10 +293,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current shadow opacity to the given one.
- Parameter opacity: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func shadow(opacity: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func shadow(opacity: CGFloat) -> MotionTransition {
return MotionTransition {
$0.shadowOpacity = Float(opacity)
}
}
......@@ -304,10 +304,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's current shadow radius to the given one.
- Parameter radius: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func shadow(radius: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func shadow(radius: CGFloat) -> MotionTransition {
return MotionTransition {
$0.shadowRadius = radius
}
}
......@@ -315,10 +315,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's contents rect to the given one.
- Parameter rect: A CGRect.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func contents(rect: CGRect) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func contents(rect: CGRect) -> MotionTransition {
return MotionTransition {
$0.contentsRect = rect
}
}
......@@ -326,10 +326,10 @@ extension MotionTransitionAnimation {
/**
Animates the view's contents scale to the given one.
- Parameter scale: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func contents(scale: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func contents(scale: CGFloat) -> MotionTransition {
return MotionTransition {
$0.contentsScale = scale
}
}
......@@ -337,10 +337,10 @@ extension MotionTransitionAnimation {
/**
The duration of the view's animation.
- Parameter _ duration: A TimeInterval.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func duration(_ duration: TimeInterval) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func duration(_ duration: TimeInterval) -> MotionTransition {
return MotionTransition {
$0.duration = duration
}
}
......@@ -349,15 +349,15 @@ extension MotionTransitionAnimation {
Sets the view's animation duration to the longest
running animation within a transition.
*/
public static var preferredDurationMatchesLongest = MotionTransitionAnimation.duration(.infinity)
public static var preferredDurationMatchesLongest = MotionTransition.duration(.infinity)
/**
Delays the animation of a given view.
- Parameter _ time: TimeInterval.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func delay(_ time: TimeInterval) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func delay(_ time: TimeInterval) -> MotionTransition {
return MotionTransition {
$0.delay = time
}
}
......@@ -365,10 +365,10 @@ extension MotionTransitionAnimation {
/**
Sets the view's timing function for the animation.
- Parameter _ timingFunction: A MotionAnimationTimingFunction.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
public static func timingFunction(_ timingFunction: MotionAnimationTimingFunction) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func timingFunction(_ timingFunction: MotionAnimationTimingFunction) -> MotionTransition {
return MotionTransition {
$0.timingFunction = timingFunction
}
}
......@@ -378,11 +378,11 @@ extension MotionTransitionAnimation {
given a stiffness and damping.
- Parameter stiffness: A CGFlloat.
- Parameter damping: A CGFloat.
- Returns: A MotionTransitionAnimation.
- Returns: A MotionTransition.
*/
@available(iOS 9, *)
public static func spring(stiffness: CGFloat, damping: CGFloat) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func spring(stiffness: CGFloat, damping: CGFloat) -> MotionTransition {
return MotionTransition {
$0.spring = (stiffness, damping)
}
}
......@@ -393,8 +393,8 @@ extension MotionTransitionAnimation {
represents a curve in an upward direction.
- Parameter intensity: A CGFloat.
*/
public static func arc(intensity: CGFloat = 1) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func arc(intensity: CGFloat = 1) -> MotionTransition {
return MotionTransition {
$0.arc = intensity
}
}
......@@ -406,9 +406,44 @@ extension MotionTransitionAnimation {
- Paramater animationDelayUntilMatchedViews: A boolean indicating whether
or not to delay the subview animation until all have started.
*/
public static func cascade(delta: TimeInterval = 0.02, direction: MotionCascadeDirection = .topToBottom, animationDelayUntilMatchedViews: Bool = false) -> MotionTransitionAnimation {
return MotionTransitionAnimation {
public static func cascade(delta: TimeInterval = 0.02, direction: MotionCascadeDirection = .topToBottom, animationDelayUntilMatchedViews: Bool = false) -> MotionTransition {
return MotionTransition {
$0.cascade = (delta, direction, animationDelayUntilMatchedViews)
}
}
}
extension MotionTransition {
/**
Applies the transition state directly to the view before the animation
begins. For source views, the state is applied immediately. For destination
views, the state is applied at the end of the transition.
*/
public static func startWith(animations: [MotionTransition]) -> MotionTransition {
return MotionTransition {
if nil == $0.startState {
$0.startState = MotionTransitionStateWrapper(state: [])
}
$0.startState!.state.append(contentsOf: animations)
}
}
/**
Applies the transition state directly to the view before the animation
begins. For source views, the state is applied immediately. For destination
views, the state is applied at the end of the transition. This only takes
affect if the view is matched.
*/
public static func startWithIfMatched(animations: [MotionTransition]) -> MotionTransition {
return MotionTransition {
if nil == $0.startStateIfMatched {
$0.startStateIfMatched = []
}
$0.startStateIfMatched!.append(contentsOf: animations)
}
}
}
......@@ -58,5 +58,5 @@ public protocol MotionTransitionAnimator: class {
func clean()
func apply(motionTransitions: [MotionTransitionAnimation], to view: UIView)
func apply(motionTransitions: [MotionTransition], to view: UIView)
}
......@@ -31,12 +31,12 @@
import UIKit
internal class MotionTransitionStateWrapper {
/// A reference to a MotionTransitionAnimationState.
/// A reference to a MotionTransitionState.
internal var state: MotionTransitionState
/**
An initializer that accepts a given MotionTransitionAnimationState.
- Parameter state: A MotionTransitionAnimationState.
An initializer that accepts a given MotionTransitionState.
- Parameter state: A MotionTransitionState.
*/
internal init(state: MotionTransitionState) {
self.state = state
......@@ -50,7 +50,7 @@ public struct MotionTransitionState {
/// A reference to the motion identifier.
public var motionIdentifier: String?
public var startStateIfMatched: [MotionTransitionAnimation]?
public var startStateIfMatched: [MotionTransition]?
public var position: CGPoint?
public var size: CGSize?
......@@ -88,20 +88,20 @@ public struct MotionTransitionState {
public var ignoreSubviewTransitionAnimations: Bool?
public var coordinateSpace: MotionCoordinateSpace?
public var useScaleBasedSizeChange: Bool?
public var snapshotType: MotionSnapshot?
public var motionSnapshot: MotionSnapshot?
public var forceAnimate: Bool = false
public var custom: [String:Any]?
init(transitionAnimations: [MotionTransitionAnimation]) {
init(transitionAnimations: [MotionTransition]) {
append(contentsOf: transitionAnimations)
}
public mutating func append(_ transitionAnimations: MotionTransitionAnimation) {
public mutating func append(_ transitionAnimations: MotionTransition) {
transitionAnimations.apply(&self)
}
public mutating func append(contentsOf transitionAnimations: [MotionTransitionAnimation]) {
public mutating func append(contentsOf transitionAnimations: [MotionTransition]) {
for v in transitionAnimations {
v.apply(&self)
}
......@@ -109,7 +109,7 @@ public struct MotionTransitionState {
}
extension MotionTransitionState: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: MotionTransitionAnimation...) {
public init(arrayLiteral elements: MotionTransition...) {
append(contentsOf: elements)
}
}
......
// The MIT License (MIT)
//
// 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
public class SourcePreprocessor: TransitionPreprocessor {
/// A reference to a MotionContext.
public weak var context: MotionContext!
/**
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
public func process(fromViews: [UIView], toViews: [UIView]) {
prepare(fromViews: fromViews)
prepare(toViews: toViews)
}
}
extension SourcePreprocessor {
/**
Prepares the from views.
- Parameter fromViews: An Array of UIViews.
*/
fileprivate func prepare(fromViews: [UIView]) {
for fv in fromViews {
guard let identifier = context.viewToMotionTransitionState[fv]?.motionIdentifier else {
continue
}
guard let tv = context.destinationIdentifierToView[identifier] else {
continue
}
prepare(view: fv, for: tv)
}
}
/**
Prepares the to views.
- Parameter fromViews: An Array of UIViews.
*/
fileprivate func prepare(toViews: [UIView]) {
for tv in context.toViews {
guard let identifier = context.viewToMotionTransitionState[tv]?.motionIdentifier else {
continue
}
guard let fv = context.sourceIdentifierToView[identifier] else {
continue
}
prepare(view: tv, for: fv)
}
}
fileprivate func prepare(view: UIView, for targetView: UIView) {
guard var state = context.viewToMotionTransitionState[view] else {
return
}
state.coordinateSpace = .global
state.position = context.container.convert(targetView.layer.position, from: targetView.superview!)
state.transform = nil
state.size = nil
state.cornerRadius = nil
if view.bounds.size != targetView.bounds.size {
state.size = targetView.bounds.size
}
if view.layer.cornerRadius != targetView.layer.cornerRadius {
state.cornerRadius = targetView.layer.cornerRadius
}
if view.layer.transform != targetView.layer.transform {
state.transform = targetView.layer.transform
}
if view.layer.shadowColor != targetView.layer.shadowColor {
state.shadowColor = targetView.layer.shadowColor
}
if view.layer.shadowOpacity != targetView.layer.shadowOpacity {
state.shadowOpacity = targetView.layer.shadowOpacity
}
if view.layer.shadowOffset != targetView.layer.shadowOffset {
state.shadowOffset = targetView.layer.shadowOffset
}
if view.layer.shadowRadius != targetView.layer.shadowRadius {
state.shadowRadius = targetView.layer.shadowRadius
}
if view.layer.shadowPath != targetView.layer.shadowPath {
state.shadowPath = targetView.layer.shadowPath
}
if view.layer.contentsRect != targetView.layer.contentsRect {
state.contentsRect = targetView.layer.contentsRect
}
if view.layer.contentsScale != targetView.layer.contentsScale {
state.contentsScale = targetView.layer.contentsScale
}
context.viewToMotionTransitionState[view] = state
}
}
......@@ -30,12 +30,12 @@
import UIKit
public protocol MotionTransitionPreprocessor: class {
public protocol TransitionPreprocessor: class {
/// A reference to a MotionContext.
weak var context: MotionContext! { get set }
/**
Implementation for processors.
Implementation for processor.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
......
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