Commit c10fe806 by Daniel Dahan

combined MotionCoreAnimator and MotionTransitionAnimator

parent 470bb0fb
......@@ -33,7 +33,6 @@
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 */; };
96E409691F24F7370015A2B5 /* MotionTransitionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409411F24F7370015A2B5 /* MotionTransitionAnimator.swift */; };
96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409421F24F7370015A2B5 /* MotionViewPropertyViewContext.swift */; };
96E4096B1F24F7370015A2B5 /* Motion+Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409441F24F7370015A2B5 /* Motion+Array.swift */; };
96E4096C1F24F7370015A2B5 /* Motion+CALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409451F24F7370015A2B5 /* Motion+CALayer.swift */; };
......@@ -64,7 +63,6 @@
96E409891F24F7570015A2B5 /* MotionAnimator.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4098A1F24F7570015A2B5 /* MotionAnimatorViewContext.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4098B1F24F7570015A2B5 /* MotionCoreAnimationViewContext.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4098D1F24F7570015A2B5 /* MotionTransitionAnimator.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409411F24F7370015A2B5 /* MotionTransitionAnimator.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4098E1F24F7570015A2B5 /* MotionViewPropertyViewContext.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409421F24F7370015A2B5 /* MotionViewPropertyViewContext.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4098F1F24F7570015A2B5 /* Motion+Array.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409441F24F7370015A2B5 /* Motion+Array.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409901F24F7570015A2B5 /* Motion+CALayer.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409451F24F7370015A2B5 /* Motion+CALayer.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -112,7 +110,6 @@
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>"; };
96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCoreAnimationViewContext.swift; sourceTree = "<group>"; };
96E409411F24F7370015A2B5 /* MotionTransitionAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransitionAnimator.swift; sourceTree = "<group>"; };
96E409421F24F7370015A2B5 /* MotionViewPropertyViewContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionViewPropertyViewContext.swift; sourceTree = "<group>"; };
96E409441F24F7370015A2B5 /* Motion+Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+Array.swift"; sourceTree = "<group>"; };
96E409451F24F7370015A2B5 /* Motion+CALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Motion+CALayer.swift"; sourceTree = "<group>"; };
......@@ -210,7 +207,6 @@
965FE96C1FDDA6400098BDD0 /* MotionCoreAnimator.swift */,
96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */,
96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */,
96E409411F24F7370015A2B5 /* MotionTransitionAnimator.swift */,
96E409421F24F7370015A2B5 /* MotionViewPropertyViewContext.swift */,
);
path = Animator;
......@@ -257,7 +253,6 @@
96E409891F24F7570015A2B5 /* MotionAnimator.swift in Headers */,
96E4098A1F24F7570015A2B5 /* MotionAnimatorViewContext.swift in Headers */,
96E4098B1F24F7570015A2B5 /* MotionCoreAnimationViewContext.swift in Headers */,
96E4098D1F24F7570015A2B5 /* MotionTransitionAnimator.swift in Headers */,
96E4098E1F24F7570015A2B5 /* MotionViewPropertyViewContext.swift in Headers */,
96E4098F1F24F7570015A2B5 /* Motion+Array.swift in Headers */,
96E409901F24F7570015A2B5 /* Motion+CALayer.swift in Headers */,
......@@ -370,7 +365,6 @@
96E409781F24F7370015A2B5 /* MotionCAAnimation.swift in Sources */,
96E409811F24F7370015A2B5 /* MotionTargetState.swift in Sources */,
96E409801F24F7370015A2B5 /* MotionTransitionObserver.swift in Sources */,
96E409691F24F7370015A2B5 /* MotionTransitionAnimator.swift in Sources */,
96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */,
96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */,
96E4096D1F24F7370015A2B5 /* Motion+CAMediaTimingFunction.swift in Sources */,
......
......@@ -30,7 +30,7 @@ import UIKit
internal class MotionAnimatorViewContext {
/// An optional reference to a MotionAnimator.
var animator: MotionCoreAnimator?
var animator: MotionAnimator?
/// A reference to the snapshot UIView.
var snapshot: UIView
......@@ -51,7 +51,7 @@ internal class MotionAnimatorViewContext {
/// A container view for the transition.
var container: UIView? {
return animator?.context.container
return animator?.motion.context.container
}
/**
......@@ -61,7 +61,7 @@ internal class MotionAnimatorViewContext {
- Parameter targetState: A MotionModifier.
- Parameter isAppearing: A Boolean.
*/
required init(animator: MotionCoreAnimator, snapshot: UIView, targetState: MotionTargetState, isAppearing: Bool) {
required init(animator: MotionAnimator, snapshot: UIView, targetState: MotionTargetState, isAppearing: Bool) {
self.animator = animator
self.snapshot = snapshot
self.targetState = targetState
......
......@@ -28,7 +28,7 @@
import UIKit
class MotionCoreAnimator: MotionAnimator {
internal class MotionCoreAnimator<T: MotionAnimatorViewContext>: MotionAnimator {
weak public var motion: MotionTransition!
/// A reference to the MotionContext.
......@@ -36,23 +36,131 @@ class MotionCoreAnimator: MotionAnimator {
return motion.context
}
func clean() {}
/// An index of views to their corresponding animator context.
var viewToContexts = [UIView: T]()
/// Cleans the contexts.
func clean() {
for v in viewToContexts.values {
v.clean()
}
viewToContexts.removeAll()
}
/**
A function that determines if a view can be animated.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
return false
guard let state = context[view] else {
return false
}
return T.canAnimate(view: view, state: state, isAppearing: isAppearing)
}
/**
Animates the fromViews to the toViews.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
- Returns: A TimeInterval.
*/
func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
return 0
var d: TimeInterval = 0
for v in fromViews {
createViewContext(view: v, isAppearing: false)
}
for v in toViews {
createViewContext(view: v, isAppearing: true)
}
for v in viewToContexts.values {
if let duration = v.targetState.duration, .infinity != duration {
v.duration = duration
d = max(d, duration)
} else {
let duration = v.snapshot.optimizedDuration(targetState: v.targetState)
if nil == v.targetState.duration {
v.duration = duration
}
d = max(d, duration)
}
}
for v in viewToContexts.values {
if .infinity == v.targetState.duration {
v.duration = d
}
d = max(d, v.startAnimations())
}
return d
}
func seek(to progress: TimeInterval) {}
/**
Moves the view's animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
func seek(to progress: TimeInterval) {
for v in viewToContexts.values {
v.seek(to: progress)
}
}
/**
Resumes the animation with a given elapsed time and
optional reversed boolean.
- Parameter at progress: A TimeInterval.
- Parameter isReversed: A boolean to reverse the animation
or not.
*/
func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
return 0
var duration: TimeInterval = 0
for (_, v) in viewToContexts {
if nil == v.targetState.duration {
v.duration = max(v.duration, v.snapshot.optimizedDuration(targetState: v.targetState) + progress)
}
duration = max(duration, v.resume(at: progress, isReversed: isReversed))
}
return duration
}
func apply(state: MotionTargetState, to view: UIView) {}
/**
Applies the given state to the given view.
- Parameter state: A MotionModifier.
- Parameter to view: A UIView.
*/
func apply(state: MotionTargetState, to view: UIView) {
guard let v = viewToContexts[view] else {
return
}
v.apply(state: state)
}
}
fileprivate extension MotionCoreAnimator {
/**
Creates a view context for a given view.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
func createViewContext(view: UIView, isAppearing: Bool) {
viewToContexts[view] = T(animator: self, snapshot: context.snapshotView(for: view), targetState: context[view]!, isAppearing: isAppearing)
}
}
/*
* 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
internal class MotionTargetStateAnimator<T: MotionAnimatorViewContext>: MotionCoreAnimator {
/// An index of views to their corresponding animator context.
var viewToContexts = [UIView: T]()
/// Cleans the contexts.
override func clean() {
for v in viewToContexts.values {
v.clean()
}
viewToContexts.removeAll()
}
/**
A function that determines if a view can be animated.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
override func canAnimate(view: UIView, isAppearing: Bool) -> Bool {
guard let state = context[view] else {
return false
}
return T.canAnimate(view: view, state: state, isAppearing: isAppearing)
}
/**
Animates the fromViews to the toViews.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
- Returns: A TimeInterval.
*/
override func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
var d: TimeInterval = 0
for v in fromViews {
createViewContext(view: v, isAppearing: false)
}
for v in toViews {
createViewContext(view: v, isAppearing: true)
}
for v in viewToContexts.values {
if let duration = v.targetState.duration, duration != .infinity {
v.duration = duration
d = max(d, duration)
} else {
let duration = v.snapshot.optimizedDuration(targetState: v.targetState)
if nil == v.targetState.duration {
v.duration = duration
}
d = max(d, duration)
}
}
for v in viewToContexts.values {
if .infinity == v.targetState.duration {
v.duration = d
}
d = max(d, v.startAnimations())
}
return d
}
/**
Moves the view's animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
override func seek(to progress: TimeInterval) {
for v in viewToContexts.values {
v.seek(to: progress)
}
}
/**
Resumes the animation with a given elapsed time and
optional reversed boolean.
- Parameter at progress: A TimeInterval.
- Parameter isReversed: A boolean to reverse the animation
or not.
*/
override func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
var duration: TimeInterval = 0
for (_, v) in viewToContexts {
if nil == v.targetState.duration {
v.duration = max(v.duration, v.snapshot.optimizedDuration(targetState: v.targetState) + progress)
}
duration = max(duration, v.resume(at: progress, isReversed: isReversed))
}
return duration
}
/**
Applies the given state to the given view.
- Parameter state: A MotionModifier.
- Parameter to view: A UIView.
*/
override func apply(state: MotionTargetState, to view: UIView) {
guard let v = viewToContexts[view] else {
return
}
v.apply(state: state)
}
}
fileprivate extension MotionTargetStateAnimator {
/**
Creates a view context for a given view.
- Parameter view: A UIView.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
func createViewContext(view: UIView, isAppearing: Bool) {
viewToContexts[view] = T(animator: self, snapshot: context.snapshotView(for: view), targetState: context[view]!, isAppearing: isAppearing)
}
}
......@@ -44,12 +44,12 @@ public final class MotionModifier {
public extension MotionModifier {
/**
Animates the view with a matching motion identifier.
- Parameter _ identifier: A String.
- Parameter _ motionIdentifier: A String.
- Returns: A MotionModifier.
*/
static func motionIdentifier(_ identifier: String) -> MotionModifier {
static func source(_ motionIdentifier: String) -> MotionModifier {
return MotionModifier {
$0.motionIdentifier = identifier
$0.motionIdentifier = motionIdentifier
}
}
......@@ -150,10 +150,9 @@ public extension MotionModifier {
*/
static func rotate(x: CGFloat = 0, y: CGFloat = 0, z: CGFloat = 0) -> MotionModifier {
return MotionModifier {
var t = $0.transform ?? CATransform3DIdentity
t = CATransform3DRotate(t, CGFloat(Double.pi) * x / 180, 1, 0, 0)
t = CATransform3DRotate(t, CGFloat(Double.pi) * y / 180, 0, 1, 0)
$0.transform = CATransform3DRotate(t, CGFloat(Double.pi) * z / 180, 0, 0, 1)
$0.transform = CATransform3DRotate($0.transform ?? CATransform3DIdentity, x, 1, 0, 0)
$0.transform = CATransform3DRotate($0.transform!, y, 0, 1, 0)
$0.transform = CATransform3DRotate($0.transform!, z, 0, 0, 1)
}
}
......@@ -267,6 +266,17 @@ public extension MotionModifier {
}
/**
Animates the view's current opacity to the given one.
- Parameter _ opacity: A Double.
- Returns: A MotionModifier.
*/
static func opacity(_ opacity: Double) -> MotionModifier {
return MotionModifier {
$0.opacity = opacity
}
}
/**
Animates the view's current zPosition to the given position.
- Parameter _ position: An Int.
- Returns: A MotionModifier.
......@@ -390,7 +400,9 @@ public extension MotionModifier {
Sets the view's animation duration to the longest
running animation within a transition.
*/
static var preferredDurationMatchesLongest = MotionModifier.duration(.infinity)
static var durationMatchLongest = MotionModifier {
$0.duration = .infinity
}
/**
Delays the animation of a given view.
......@@ -545,7 +557,7 @@ public extension MotionModifier {
*/
static func beginWith(_ modifiers: [MotionModifier]) -> MotionModifier {
return MotionModifier {
if $0.beginState == nil {
if nil == $0.beginState {
$0.beginState = []
}
......
......@@ -336,8 +336,8 @@ class TransitionPreprocessor: MotionCorePreprocessor {
}
#endif
context[tv]!.append(.preferredDurationMatchesLongest)
context[fv]!.append(.preferredDurationMatchesLongest)
context[tv]!.append(.durationMatchLongest)
context[fv]!.append(.durationMatchLongest)
case .zoom:
context.insertToViewFirst = true
......
......@@ -134,10 +134,10 @@ fileprivate extension MotionTransition {
/// Prepares the animators.
func prepareAnimators() {
animators = [MotionTargetStateAnimator<MotionCoreAnimationViewContext>()]
animators = [MotionCoreAnimator<MotionCoreAnimationViewContext>()]
if #available(iOS 10, tvOS 10, *) {
animators.append(MotionTargetStateAnimator<MotionViewPropertyViewContext>())
animators.append(MotionCoreAnimator<MotionViewPropertyViewContext>())
}
}
......
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