Commit 100bf243 by Daniel Dahan

updated to 2 space indentation

parent 654928d7
......@@ -29,47 +29,47 @@
import UIKit
public protocol MotionAnimator: class {
/// A reference to Motion.
weak var motion: MotionTransition! { get set }
/// Cleans the contexts.
func clean()
/**
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
/**
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
/**
Moves the view's animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
func seek(to progress: TimeInterval)
/**
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
/**
Applies the given state to the given view.
- Parameter state: A MotionModifier.
- Parameter to view: A UIView.
*/
func apply(state: MotionTargetState, to view: UIView)
/// A reference to Motion.
weak var motion: MotionTransition! { get set }
/// Cleans the contexts.
func clean()
/**
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
/**
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
/**
Moves the view's animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
func seek(to progress: TimeInterval)
/**
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
/**
Applies the given state to the given view.
- Parameter state: A MotionModifier.
- Parameter to view: A UIView.
*/
func apply(state: MotionTargetState, to view: UIView)
}
......@@ -29,94 +29,94 @@
import UIKit
internal class MotionAnimatorViewContext {
/// An optional reference to a MotionAnimator.
var animator: MotionAnimator?
/// A reference to the snapshot UIView.
var snapshot: UIView
/// The animation target state.
var targetState: MotionTargetState
/// A boolean indicating if the view is appearing.
var isAppearing: Bool
/// Animation duration time.
var duration: TimeInterval = 0
/// The computed current time of the snapshot layer.
var currentTime: TimeInterval {
return snapshot.layer.convertTime(CACurrentMediaTime(), from: nil)
}
/// A container view for the transition.
var container: UIView? {
return animator?.motion.context.container
}
/**
An initializer.
- Parameter animator: A MotionAnimator.
- Parameter snapshot: A UIView.
- Parameter targetState: A MotionModifier.
- Parameter isAppearing: A Boolean.
*/
required init(animator: MotionAnimator, snapshot: UIView, targetState: MotionTargetState, isAppearing: Bool) {
self.animator = animator
self.snapshot = snapshot
self.targetState = targetState
self.isAppearing = isAppearing
}
/// Cleans the context.
func clean() {
animator = nil
}
/**
A class function that determines if a view can be animated
to a given state.
- Parameter view: A UIView.
- Parameter state: A MotionModifier.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
class func canAnimate(view: UIView, state: MotionTargetState, isAppearing: Bool) -> Bool {
return false
}
/**
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.
- Returns: A TimeInterval.
*/
@discardableResult
func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
return 0
}
/**
Moves the animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
func seek(to progress: TimeInterval) {}
/**
Applies the given state to the target state.
- Parameter state: A MotionModifier.
*/
func apply(state: MotionTargetState) {}
/**
Starts the animations with an appearing boolean flag.
- Parameter isAppearing: A boolean value whether the view
is appearing or not.
*/
@discardableResult
func startAnimations() -> TimeInterval {
return 0
}
/// An optional reference to a MotionAnimator.
var animator: MotionAnimator?
/// A reference to the snapshot UIView.
var snapshot: UIView
/// The animation target state.
var targetState: MotionTargetState
/// A boolean indicating if the view is appearing.
var isAppearing: Bool
/// Animation duration time.
var duration: TimeInterval = 0
/// The computed current time of the snapshot layer.
var currentTime: TimeInterval {
return snapshot.layer.convertTime(CACurrentMediaTime(), from: nil)
}
/// A container view for the transition.
var container: UIView? {
return animator?.motion.context.container
}
/**
An initializer.
- Parameter animator: A MotionAnimator.
- Parameter snapshot: A UIView.
- Parameter targetState: A MotionModifier.
- Parameter isAppearing: A Boolean.
*/
required init(animator: MotionAnimator, snapshot: UIView, targetState: MotionTargetState, isAppearing: Bool) {
self.animator = animator
self.snapshot = snapshot
self.targetState = targetState
self.isAppearing = isAppearing
}
/// Cleans the context.
func clean() {
animator = nil
}
/**
A class function that determines if a view can be animated
to a given state.
- Parameter view: A UIView.
- Parameter state: A MotionModifier.
- Parameter isAppearing: A boolean that determines whether the
view is appearing.
*/
class func canAnimate(view: UIView, state: MotionTargetState, isAppearing: Bool) -> Bool {
return false
}
/**
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.
- Returns: A TimeInterval.
*/
@discardableResult
func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
return 0
}
/**
Moves the animation to the given elapsed time.
- Parameter to progress: A TimeInterval.
*/
func seek(to progress: TimeInterval) {}
/**
Applies the given state to the target state.
- Parameter state: A MotionModifier.
*/
func apply(state: MotionTargetState) {}
/**
Starts the animations with an appearing boolean flag.
- Parameter isAppearing: A boolean value whether the view
is appearing or not.
*/
@discardableResult
func startAnimations() -> TimeInterval {
return 0
}
}
......@@ -29,138 +29,138 @@
import UIKit
internal class MotionCoreAnimator<T: MotionAnimatorViewContext>: MotionAnimator {
weak public var motion: MotionTransition!
weak public var motion: MotionTransition!
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion.context
}
/// 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()
}
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion.context
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 {
guard let state = context[view] else {
return false
}
/// An index of views to their corresponding animator context.
var viewToContexts = [UIView: T]()
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 {
var d: TimeInterval = 0
/// Cleans the contexts.
func clean() {
for v in viewToContexts.values {
v.clean()
}
viewToContexts.removeAll()
for v in fromViews {
createViewContext(view: v, isAppearing: false)
}
/**
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 {
guard let state = context[view] else {
return false
}
return T.canAnimate(view: view, state: state, isAppearing: isAppearing)
for v in toViews {
createViewContext(view: v, isAppearing: true)
}
/**
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 {
var d: TimeInterval = 0
for v in viewToContexts.values {
if let duration = v.targetState.duration, .infinity != duration {
v.duration = duration
d = max(d, duration)
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)
}
}
} else {
let duration = v.snapshot.optimizedDuration(targetState: v.targetState)
for v in viewToContexts.values {
if .infinity == v.targetState.duration {
v.duration = d
}
d = max(d, v.startAnimations())
if nil == v.targetState.duration {
v.duration = duration
}
return d
d = max(d, duration)
}
}
/**
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)
}
for v in viewToContexts.values {
if .infinity == v.targetState.duration {
v.duration = d
}
d = max(d, v.startAnimations())
}
/**
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 {
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
return d
}
/**
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 {
var duration: TimeInterval = 0
/**
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)
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.
*/
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)
}
/**
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)
}
}
......@@ -30,76 +30,76 @@ import UIKit
@available(iOS 10, tvOS 10, *)
internal class MotionViewPropertyViewContext: MotionAnimatorViewContext {
/// A reference to the UIViewPropertyAnimator.
fileprivate var viewPropertyAnimator: UIViewPropertyAnimator!
/// Ending effect.
fileprivate var endEffect: UIVisualEffect?
/// Starting effect.
fileprivate var startEffect: UIVisualEffect?
override class func canAnimate(view: UIView, state: MotionTargetState, isAppearing: Bool) -> Bool {
return view is UIVisualEffectView && nil != state.opacity
/// A reference to the UIViewPropertyAnimator.
fileprivate var viewPropertyAnimator: UIViewPropertyAnimator!
/// Ending effect.
fileprivate var endEffect: UIVisualEffect?
/// Starting effect.
fileprivate var startEffect: UIVisualEffect?
override class func canAnimate(view: UIView, state: MotionTargetState, isAppearing: Bool) -> Bool {
return view is UIVisualEffectView && nil != state.opacity
}
override func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
guard let visualEffectView = snapshot as? UIVisualEffectView else {
return 0
}
override func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval {
guard let visualEffectView = snapshot as? UIVisualEffectView else {
return 0
}
if isReversed {
viewPropertyAnimator?.stopAnimation(false)
viewPropertyAnimator?.finishAnimation(at: .current)
viewPropertyAnimator = UIViewPropertyAnimator(duration: duration, curve: .linear) { [weak self] in
guard let `self` = self else {
return
}
visualEffectView.effect = isReversed ? self.startEffect : self.endEffect
}
if isReversed {
viewPropertyAnimator?.stopAnimation(false)
viewPropertyAnimator?.finishAnimation(at: .current)
viewPropertyAnimator = UIViewPropertyAnimator(duration: duration, curve: .linear) { [weak self] in
guard let `self` = self else {
return
}
viewPropertyAnimator.startAnimation()
return duration
}
override func seek(to progress: TimeInterval) {
viewPropertyAnimator?.pauseAnimation()
viewPropertyAnimator?.fractionComplete = CGFloat(progress / duration)
visualEffectView.effect = isReversed ? self.startEffect : self.endEffect
}
}
override func clean() {
super.clean()
viewPropertyAnimator?.stopAnimation(false)
viewPropertyAnimator?.finishAnimation(at: .current)
viewPropertyAnimator = nil
viewPropertyAnimator.startAnimation()
return duration
}
override func seek(to progress: TimeInterval) {
viewPropertyAnimator?.pauseAnimation()
viewPropertyAnimator?.fractionComplete = CGFloat(progress / duration)
}
override func clean() {
super.clean()
viewPropertyAnimator?.stopAnimation(false)
viewPropertyAnimator?.finishAnimation(at: .current)
viewPropertyAnimator = nil
}
override func startAnimations() -> TimeInterval {
guard let visualEffectView = snapshot as? UIVisualEffectView else {
return 0
}
override func startAnimations() -> TimeInterval {
guard let visualEffectView = snapshot as? UIVisualEffectView else {
return 0
}
let appearedEffect = visualEffectView.effect
let disappearedEffect = 0 == targetState.opacity ? nil : visualEffectView.effect
startEffect = isAppearing ? disappearedEffect : appearedEffect
endEffect = isAppearing ? appearedEffect : disappearedEffect
visualEffectView.effect = startEffect
viewPropertyAnimator = UIViewPropertyAnimator(duration: duration, curve: .linear) { [weak self] in
guard let `self` = self else {
return
}
visualEffectView.effect = self.endEffect
}
viewPropertyAnimator.startAnimation()
return duration
let appearedEffect = visualEffectView.effect
let disappearedEffect = 0 == targetState.opacity ? nil : visualEffectView.effect
startEffect = isAppearing ? disappearedEffect : appearedEffect
endEffect = isAppearing ? appearedEffect : disappearedEffect
visualEffectView.effect = startEffect
viewPropertyAnimator = UIViewPropertyAnimator(duration: duration, curve: .linear) { [weak self] in
guard let `self` = self else {
return
}
visualEffectView.effect = self.endEffect
}
viewPropertyAnimator.startAnimation()
return duration
}
}
......@@ -29,16 +29,16 @@
import UIKit
internal extension Array {
/**
Retrieves the element at the given index if it exists.
- Parameter _ index: An Int.
- Returns: An optional Element value.
*/
func get(_ index: Int) -> Element? {
if index < count {
return self[index]
}
return nil
/**
Retrieves the element at the given index if it exists.
- Parameter _ index: An Int.
- Returns: An optional Element value.
*/
func get(_ index: Int) -> Element? {
if index < count {
return self[index]
}
return nil
}
}
......@@ -29,18 +29,18 @@
import UIKit
public extension CAMediaTimingFunction {
// Default
static let linear = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
static let easeIn = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
static let easeOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
static let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// Material
static let standard = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0)
static let deceleration = CAMediaTimingFunction(controlPoints: 0.0, 0.0, 0.2, 1)
static let acceleration = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1, 1)
static let sharp = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.6, 1)
// Easing.net
static let easeOutBack = CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.75)
// Default
static let linear = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
static let easeIn = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
static let easeOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
static let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// Material
static let standard = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0)
static let deceleration = CAMediaTimingFunction(controlPoints: 0.0, 0.0, 0.2, 1)
static let acceleration = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1, 1)
static let sharp = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.6, 1)
// Easing.net
static let easeOutBack = CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.75)
}
......@@ -24,31 +24,31 @@
*/
public struct AssociatedObject {
/**
Gets the Obj-C reference for the instance object within the UIView extension.
- Parameter base: Base object.
- Parameter key: Memory key pointer.
- Parameter initializer: Object initializer.
- Returns: The associated reference for the initializer object.
*/
public static func get<T: Any>(base: Any, key: UnsafePointer<UInt8>, initializer: () -> T) -> T {
if let v = objc_getAssociatedObject(base, key) as? T {
return v
}
let v = initializer()
objc_setAssociatedObject(base, key, v, .OBJC_ASSOCIATION_RETAIN)
return v
/**
Gets the Obj-C reference for the instance object within the UIView extension.
- Parameter base: Base object.
- Parameter key: Memory key pointer.
- Parameter initializer: Object initializer.
- Returns: The associated reference for the initializer object.
*/
public static func get<T: Any>(base: Any, key: UnsafePointer<UInt8>, initializer: () -> T) -> T {
if let v = objc_getAssociatedObject(base, key) as? T {
return v
}
/**
Sets the Obj-C reference for the instance object within the UIView extension.
- Parameter base: Base object.
- Parameter key: Memory key pointer.
- Parameter value: The object instance to set for the associated object.
- Returns: The associated reference for the initializer object.
*/
public static func set<T: Any>(base: Any, key: UnsafePointer<UInt8>, value: T) {
objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
}
let v = initializer()
objc_setAssociatedObject(base, key, v, .OBJC_ASSOCIATION_RETAIN)
return v
}
/**
Sets the Obj-C reference for the instance object within the UIView extension.
- Parameter base: Base object.
- Parameter key: Memory key pointer.
- Parameter value: The object instance to set for the associated object.
- Returns: The associated reference for the initializer object.
*/
public static func set<T: Any>(base: Any, key: UnsafePointer<UInt8>, value: T) {
objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
}
}
......@@ -32,27 +32,27 @@ fileprivate let parameterRegex = "(?:\\-?\\d+(\\.?\\d+)?)|\\w+"
fileprivate let transitionsRegex = "(\\w+)(?:\\(([^\\)]*)\\))?"
internal extension NSObject {
/// Copies an object using NSKeyedArchiver.
func copyWithArchiver() -> Any? {
return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self))!
}
/// Copies an object using NSKeyedArchiver.
func copyWithArchiver() -> Any? {
return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self))!
}
}
internal extension UIColor {
/// A tuple of the rgba components.
var components: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
return (r, g, b, a)
}
/// A tuple of the rgba components.
var components: (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
/// The alpha component value.
var alphaComponent: CGFloat {
return components.a
}
getRed(&r, green: &g, blue: &b, alpha: &a)
return (r, g, b, a)
}
/// The alpha component value.
var alphaComponent: CGFloat {
return components.a
}
}
......@@ -27,10 +27,10 @@ import UIKit
@objc(MotionAnimationFillMode)
public enum MotionAnimationFillMode: Int {
case forwards
case backwards
case both
case removed
case forwards
case backwards
case both
case removed
}
/**
......@@ -38,14 +38,14 @@ public enum MotionAnimationFillMode: Int {
- Parameter mode: An MotionAnimationFillMode enum value.
*/
public func MotionAnimationFillModeToValue(mode: MotionAnimationFillMode) -> String {
switch mode {
case .forwards:
return kCAFillModeForwards
case .backwards:
return kCAFillModeBackwards
case .both:
return kCAFillModeBoth
case .removed:
return kCAFillModeRemoved
}
switch mode {
case .forwards:
return kCAFillModeForwards
case .backwards:
return kCAFillModeBackwards
case .both:
return kCAFillModeBoth
case .removed:
return kCAFillModeRemoved
}
}
......@@ -29,122 +29,122 @@
import UIKit
public struct MotionAnimationState {
/// A reference to the position.
public var position: CGPoint?
/// A reference to the size.
public var size: CGSize?
/// A reference to the transform.
public var transform: CATransform3D?
/// A reference to the spin tuple.
public var spin: (x: CGFloat, y: CGFloat, z: CGFloat)?
/// A reference to the opacity.
public var opacity: Double?
/// A reference to the cornerRadius.
public var cornerRadius: CGFloat?
/// A reference to the backgroundColor.
public var backgroundColor: CGColor?
/// A reference to the zPosition.
public var zPosition: CGFloat?
/// A reference to the borderWidth.
public var borderWidth: CGFloat?
/// A reference to the borderColor.
public var borderColor: CGColor?
/// A reference to the shadowColor.
public var shadowColor: CGColor?
/// A reference to the shadowOpacity.
public var shadowOpacity: Float?
/// A reference to the shadowOffset.
public var shadowOffset: CGSize?
/// A reference to the shadowRadius.
public var shadowRadius: CGFloat?
/// A reference to the shadowPath.
public var shadowPath: CGPath?
/// A reference to the spring animation settings.
public var spring: (CGFloat, CGFloat)?
/// A time delay on starting the animation.
public var delay: TimeInterval = 0
/// The duration of the animation.
public var duration: TimeInterval = 0.35
/// The timing function value of the animation.
public var timingFunction = CAMediaTimingFunction.easeInOut
/// Custom target states.
public var custom: [String: Any]?
/// Completion block.
public var completion: (() -> Void)?
/**
An initializer that accepts an Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
*/
init(animations: [MotionAnimation]) {
append(contentsOf: animations)
}
/// A reference to the position.
public var position: CGPoint?
/// A reference to the size.
public var size: CGSize?
/// A reference to the transform.
public var transform: CATransform3D?
/// A reference to the spin tuple.
public var spin: (x: CGFloat, y: CGFloat, z: CGFloat)?
/// A reference to the opacity.
public var opacity: Double?
/// A reference to the cornerRadius.
public var cornerRadius: CGFloat?
/// A reference to the backgroundColor.
public var backgroundColor: CGColor?
/// A reference to the zPosition.
public var zPosition: CGFloat?
/// A reference to the borderWidth.
public var borderWidth: CGFloat?
/// A reference to the borderColor.
public var borderColor: CGColor?
/// A reference to the shadowColor.
public var shadowColor: CGColor?
/// A reference to the shadowOpacity.
public var shadowOpacity: Float?
/// A reference to the shadowOffset.
public var shadowOffset: CGSize?
/// A reference to the shadowRadius.
public var shadowRadius: CGFloat?
/// A reference to the shadowPath.
public var shadowPath: CGPath?
/// A reference to the spring animation settings.
public var spring: (CGFloat, CGFloat)?
/// A time delay on starting the animation.
public var delay: TimeInterval = 0
/// The duration of the animation.
public var duration: TimeInterval = 0.35
/// The timing function value of the animation.
public var timingFunction = CAMediaTimingFunction.easeInOut
/// Custom target states.
public var custom: [String: Any]?
/// Completion block.
public var completion: (() -> Void)?
/**
An initializer that accepts an Array of MotionAnimations.
- Parameter animations: An Array of MotionAnimations.
*/
init(animations: [MotionAnimation]) {
append(contentsOf: animations)
}
}
extension MotionAnimationState {
/**
Adds a MotionAnimation to the current state.
- Parameter _ element: A MotionAnimation.
*/
public mutating func append(_ element: MotionAnimation) {
element.apply(&self)
/**
Adds a MotionAnimation to the current state.
- Parameter _ element: A MotionAnimation.
*/
public mutating func append(_ element: MotionAnimation) {
element.apply(&self)
}
/**
Adds an Array of MotionAnimations to the current state.
- Parameter contentsOf elements: An Array of MotionAnimations.
*/
public mutating func append(contentsOf elements: [MotionAnimation]) {
for v in elements {
v.apply(&self)
}
/**
Adds an Array of MotionAnimations to the current state.
- Parameter contentsOf elements: An Array of MotionAnimations.
*/
public mutating func append(contentsOf elements: [MotionAnimation]) {
for v in elements {
v.apply(&self)
}
}
/**
A subscript that returns a custom value for a specified key.
- Parameter key: A String.
- Returns: An optional Any value.
*/
public subscript(key: String) -> Any? {
get {
return custom?[key]
}
/**
A subscript that returns a custom value for a specified key.
- Parameter key: A String.
- Returns: An optional Any value.
*/
public subscript(key: String) -> Any? {
get {
return custom?[key]
}
set(value) {
if nil == custom {
custom = [:]
}
custom![key] = value
}
set(value) {
if nil == custom {
custom = [:]
}
custom![key] = value
}
}
}
extension MotionAnimationState: ExpressibleByArrayLiteral {
/**
An initializer implementing the ExpressibleByArrayLiteral protocol.
- Parameter arrayLiteral elements: A list of MotionAnimations.
*/
public init(arrayLiteral elements: MotionAnimation...) {
append(contentsOf: elements)
}
/**
An initializer implementing the ExpressibleByArrayLiteral protocol.
- Parameter arrayLiteral elements: A list of MotionAnimations.
*/
public init(arrayLiteral elements: MotionAnimation...) {
append(contentsOf: elements)
}
}
......@@ -29,6 +29,6 @@
import UIKit
public enum MotionCoordinateSpace {
case global
case local
case global
case local
}
......@@ -30,84 +30,84 @@ import UIKit
class MotionPlugin: MotionCorePreprocessor, MotionAnimator {
/**
Determines whether or not to receive `seekTo` callback on every frame.
Determines whether or not to receive `seekTo` callback on every frame.
Default is false.
Default is false.
When **requirePerFrameCallback** is **false**, the plugin needs to start its own animations inside `animate` & `resume`
The `seekTo` method is only being called during an interactive transition.
When **requirePerFrameCallback** is **false**, the plugin needs to start its own animations inside `animate` & `resume`
The `seekTo` method is only being called during an interactive transition.
When **requirePerFrameCallback** is **true**, the plugin will receive `seekTo` callback on every animation frame. Hence it is possible for the plugin to do per-frame animations without implementing `animate` & `resume`
When **requirePerFrameCallback** is **true**, the plugin will receive `seekTo` callback on every animation frame. Hence it is possible for the plugin to do per-frame animations without implementing `animate` & `resume`
*/
open var requirePerFrameCallback = false
public override required init() {}
/**
Called before any animation.
Override this method when you want to preprocess transitions for views
- Parameters:
- context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController
- toViews: A flattened list of all views from destination ViewController
- context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController
- toViews: A flattened list of all views from destination ViewController
To check a view's transitions:
context[view]
context[view, "transitionName"]
context[view]
context[view, "transitionName"]
To set a view's transitions:
context[view] = [("transition1", ["parameter1"]), ("transition2", [])]
context[view, "transition1"] = ["parameter1", "parameter2"]
*/
context[view] = [("transition1", ["parameter1"]), ("transition2", [])]
context[view, "transition1"] = ["parameter1", "parameter2"]
*/
open override func process(fromViews: [UIView], toViews: [UIView]) {}
/**
- Returns: return true if the plugin can handle animating the view.
- Parameters:
- context: object holding all parsed and changed transitions,
- view: the view to check whether or not the plugin can handle the animation
- isAppearing: true if the view is isAppearing(i.e. a view in destination ViewController)
- context: object holding all parsed and changed transitions,
- view: the view to check whether or not the plugin can handle the animation
- isAppearing: true if the view is isAppearing(i.e. a view in destination ViewController)
If return true, Motion won't animate and won't let any other plugins animate this view.
The view will also be hidden automatically during the animation.
*/
open func canAnimate(view: UIView, isAppearing: Bool) -> Bool { return false }
/**
Perform the animation.
Note: views in `fromViews` & `toViews` are hidden already. Unhide then if you need to take snapshots.
- Parameters:
- context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController (filtered by `canAnimate`)
- toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`)
- context: object holding all parsed and changed transitions,
- fromViews: A flattened list of all views from source ViewController (filtered by `canAnimate`)
- toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`)
- Returns: The duration needed to complete the animation
*/
open func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval { return 0 }
/**
Called when all animations are completed.
Should perform cleanup and release any reference
*/
open func clean() {}
/**
For supporting interactive animation only.
This method is called when an interactive animation is in place
The plugin should pause the animation, and seek to the given progress
- Parameters:
- progress: time of the animation to seek to.
- progress: time of the animation to seek to.
*/
open func seek(to progress: TimeInterval) {}
/**
For supporting interactive animation only.
This method is called when an interactive animation is ended
The plugin should resume the animation.
- Parameters:
......@@ -115,42 +115,42 @@ class MotionPlugin: MotionCorePreprocessor, MotionAnimator {
- reverse: a boolean value indicating whether or not the animation should reverse
*/
open func resume(at progress: TimeInterval, isReversed: Bool) -> TimeInterval { return 0 }
/**
For supporting interactive animation only.
This method is called when user wants to override animation transitions during an interactive animation
- Parameters:
- state: the target state to override
- view: the view to override
- state: the target state to override
- view: the view to override
*/
open func apply(state: MotionTargetState, to view: UIView) {}
}
// methods for enable/disable the current plugin
extension MotionPlugin {
/// A boolean indicating whether plugins are available.
public static var isEnabled: Bool {
get {
return MotionTransition.isEnabled(plugin: self)
}
set(value) {
if value {
enable()
} else {
disable()
}
}
}
/// Enable plugins.
public static func enable() {
MotionTransition.enable(plugin: self)
/// A boolean indicating whether plugins are available.
public static var isEnabled: Bool {
get {
return MotionTransition.isEnabled(plugin: self)
}
/// Disable plugins.
public static func disable() {
MotionTransition.disable(plugin: self)
set(value) {
if value {
enable()
} else {
disable()
}
}
}
/// Enable plugins.
public static func enable() {
MotionTransition.enable(plugin: self)
}
/// Disable plugins.
public static func disable() {
MotionTransition.disable(plugin: self)
}
}
......@@ -29,24 +29,24 @@
import UIKit
public enum MotionSnapshotType {
/**
This setting will optimize for different types of views.
For custom views or views with masking, .optimizedDefault might
create snapshots that appear differently than the actual view.
In that case, use .normal or .slowRender to disable the optimization.
*/
case optimized
/// snapshotView(afterScreenUpdates:)
case normal
/// layer.render(in: currentContext)
case layerRender
/**
This setting will not create a snapshot. It will animate the view directly.
This will mess up the view hierarchy, therefore, view controllers have to rebuild
their view structure after the transition finishes.
*/
case noSnapshot
/**
This setting will optimize for different types of views.
For custom views or views with masking, .optimizedDefault might
create snapshots that appear differently than the actual view.
In that case, use .normal or .slowRender to disable the optimization.
*/
case optimized
/// snapshotView(afterScreenUpdates:)
case normal
/// layer.render(in: currentContext)
case layerRender
/**
This setting will not create a snapshot. It will animate the view directly.
This will mess up the view hierarchy, therefore, view controllers have to rebuild
their view structure after the transition finishes.
*/
case noSnapshot
}
......@@ -29,166 +29,166 @@
import UIKit
public struct MotionTargetState {
/// The identifier value to match source and destination views.
public var motionIdentifier: String?
/// The initial state that the transition will start at.
internal var beginState: [MotionModifier]?
public var conditionalModifiers: [((MotionConditionalContext) -> Bool, [MotionModifier])]?
/// A reference to the position.
public var position: CGPoint?
/// A reference to the size.
public var size: CGSize?
/// A reference to the transform.
public var transform: CATransform3D?
/// A reference to the opacity.
public var opacity: Double?
/// A reference to the cornerRadius.
public var cornerRadius: CGFloat?
/// A reference to the backgroundColor.
public var backgroundColor: CGColor?
/// A reference to the zPosition.
public var zPosition: CGFloat?
/// A reference to the contentsRect.
public var contentsRect: CGRect?
/// A reference to the contentsScale.
public var contentsScale: CGFloat?
/// A reference to the borderWidth.
public var borderWidth: CGFloat?
/// A reference to the borderColor.
public var borderColor: CGColor?
/// A reference to the shadowColor.
public var shadowColor: CGColor?
/// A reference to the shadowOpacity.
public var shadowOpacity: Float?
/// A reference to the shadowOffset.
public var shadowOffset: CGSize?
/// A reference to the shadowRadius.
public var shadowRadius: CGFloat?
/// A reference to the shadowPath.
public var shadowPath: CGPath?
/// A boolean for the masksToBounds state.
public var masksToBounds: Bool?
/// A boolean indicating whether to display a shadow or not.
public var displayShadow = true
/// A reference to the overlay settings.
public var overlay: (color: CGColor, opacity: CGFloat)?
/// A reference to the spring animation settings.
public var spring: (CGFloat, CGFloat)?
/// A time delay on starting the animation.
public var delay: TimeInterval = 0
/// The duration of the animation.
public var duration: TimeInterval?
/// The timing function value of the animation.
public var timingFunction: CAMediaTimingFunction?
/// The arc curve value.
public var arc: CGFloat?
/// The cascading animation settings.
public var cascade: (TimeInterval, CascadeDirection, Bool)?
/**
A boolean indicating whether to ignore the subview transition
animations or not.
*/
public var ignoreSubviewTransitions: Bool?
/// The coordinate space to transition views within.
public var coordinateSpace: MotionCoordinateSpace?
/// Change the size of a view based on a scale factor.
public var useScaleBasedSizeChange: Bool?
/// The type of snapshot to use.
public var snapshotType: MotionSnapshotType?
/// Do not fade the view when transitioning.
public var nonFade = false
/// Force an animation.
public var forceAnimate = false
/// Custom target states.
public var custom: [String: Any]?
/**
An initializer that accepts an Array of MotionTargetStates.
- Parameter transitions: An Array of MotionTargetStates.
*/
init(modifiers: [MotionModifier]) {
append(contentsOf: modifiers)
}
/// The identifier value to match source and destination views.
public var motionIdentifier: String?
/// The initial state that the transition will start at.
internal var beginState: [MotionModifier]?
public var conditionalModifiers: [((MotionConditionalContext) -> Bool, [MotionModifier])]?
/// A reference to the position.
public var position: CGPoint?
/// A reference to the size.
public var size: CGSize?
/// A reference to the transform.
public var transform: CATransform3D?
/// A reference to the opacity.
public var opacity: Double?
/// A reference to the cornerRadius.
public var cornerRadius: CGFloat?
/// A reference to the backgroundColor.
public var backgroundColor: CGColor?
/// A reference to the zPosition.
public var zPosition: CGFloat?
/// A reference to the contentsRect.
public var contentsRect: CGRect?
/// A reference to the contentsScale.
public var contentsScale: CGFloat?
/// A reference to the borderWidth.
public var borderWidth: CGFloat?
/// A reference to the borderColor.
public var borderColor: CGColor?
/// A reference to the shadowColor.
public var shadowColor: CGColor?
/// A reference to the shadowOpacity.
public var shadowOpacity: Float?
/// A reference to the shadowOffset.
public var shadowOffset: CGSize?
/// A reference to the shadowRadius.
public var shadowRadius: CGFloat?
/// A reference to the shadowPath.
public var shadowPath: CGPath?
/// A boolean for the masksToBounds state.
public var masksToBounds: Bool?
/// A boolean indicating whether to display a shadow or not.
public var displayShadow = true
/// A reference to the overlay settings.
public var overlay: (color: CGColor, opacity: CGFloat)?
/// A reference to the spring animation settings.
public var spring: (CGFloat, CGFloat)?
/// A time delay on starting the animation.
public var delay: TimeInterval = 0
/// The duration of the animation.
public var duration: TimeInterval?
/// The timing function value of the animation.
public var timingFunction: CAMediaTimingFunction?
/// The arc curve value.
public var arc: CGFloat?
/// The cascading animation settings.
public var cascade: (TimeInterval, CascadeDirection, Bool)?
/**
A boolean indicating whether to ignore the subview transition
animations or not.
*/
public var ignoreSubviewTransitions: Bool?
/// The coordinate space to transition views within.
public var coordinateSpace: MotionCoordinateSpace?
/// Change the size of a view based on a scale factor.
public var useScaleBasedSizeChange: Bool?
/// The type of snapshot to use.
public var snapshotType: MotionSnapshotType?
/// Do not fade the view when transitioning.
public var nonFade = false
/// Force an animation.
public var forceAnimate = false
/// Custom target states.
public var custom: [String: Any]?
/**
An initializer that accepts an Array of MotionTargetStates.
- Parameter transitions: An Array of MotionTargetStates.
*/
init(modifiers: [MotionModifier]) {
append(contentsOf: modifiers)
}
}
extension MotionTargetState {
/**
Adds a MotionTargetState to the current state.
- Parameter _ modifier: A MotionTargetState.
*/
public mutating func append(_ modifier: MotionModifier) {
modifier.apply(&self)
/**
Adds a MotionTargetState to the current state.
- Parameter _ modifier: A MotionTargetState.
*/
public mutating func append(_ modifier: MotionModifier) {
modifier.apply(&self)
}
/**
Adds an Array of MotionTargetStates to the current state.
- Parameter contentsOf modifiers: An Array of MotionTargetStates.
*/
public mutating func append(contentsOf modifiers: [MotionModifier]) {
for v in modifiers {
v.apply(&self)
}
/**
Adds an Array of MotionTargetStates to the current state.
- Parameter contentsOf modifiers: An Array of MotionTargetStates.
*/
public mutating func append(contentsOf modifiers: [MotionModifier]) {
for v in modifiers {
v.apply(&self)
}
}
/**
A subscript that returns a custom value for a specified key.
- Parameter key: A String.
- Returns: An optional Any value.
*/
public subscript(key: String) -> Any? {
get {
return custom?[key]
}
/**
A subscript that returns a custom value for a specified key.
- Parameter key: A String.
- Returns: An optional Any value.
*/
public subscript(key: String) -> Any? {
get {
return custom?[key]
}
set(value) {
if nil == custom {
custom = [:]
}
custom![key] = value
}
set(value) {
if nil == custom {
custom = [:]
}
custom![key] = value
}
}
}
extension MotionTargetState: ExpressibleByArrayLiteral {
/**
An initializer implementing the ExpressibleByArrayLiteral protocol.
- Parameter arrayLiteral elements: A list of MotionTargetStates.
*/
public init(arrayLiteral elements: MotionModifier...) {
append(contentsOf: elements)
}
/**
An initializer implementing the ExpressibleByArrayLiteral protocol.
- Parameter arrayLiteral elements: A list of MotionTargetStates.
*/
public init(arrayLiteral elements: MotionModifier...) {
append(contentsOf: elements)
}
}
......@@ -29,10 +29,10 @@
import Foundation
public protocol MotionTargetStateObserver {
/**
Executed when the elapsed time changes during a transition.
- Parameter transitionObserver: A MotionTargetStateObserver.
- Parameter didUpdateWith progress: A TimeInterval.
*/
func motion(transitionObserver: MotionTargetStateObserver, didUpdateWith progress: TimeInterval)
/**
Executed when the elapsed time changes during a transition.
- Parameter transitionObserver: A MotionTargetStateObserver.
- Parameter didUpdateWith progress: A TimeInterval.
*/
func motion(transitionObserver: MotionTargetStateObserver, didUpdateWith progress: TimeInterval)
}
......@@ -28,8 +28,8 @@
@objc(MotionViewOrderStrategy)
public enum MotionViewOrderStrategy: Int {
case auto
case sourceViewOnTop
case destinationViewOnTop
case auto
case sourceViewOnTop
case destinationViewOnTop
}
......@@ -29,100 +29,100 @@
import UIKit
public enum CascadeDirection {
case topToBottom
case bottomToTop
case leftToRight
case rightToLeft
case radial(center: CGPoint)
case inverseRadial(center: CGPoint)
case topToBottom
case bottomToTop
case leftToRight
case rightToLeft
case radial(center: CGPoint)
case inverseRadial(center: CGPoint)
/// Based on the cascade direction a comparator is set.
var comparator: (UIView, UIView) -> Bool {
switch self {
case .topToBottom:
return {
return $0.frame.minY < $1.frame.minY
}
case .bottomToTop:
return {
return $0.frame.maxY == $1.frame.maxY ? $0.frame.maxX > $1.frame.maxX : $0.frame.maxY > $1.frame.maxY
}
case .leftToRight:
return {
return $0.frame.minX < $1.frame.minX
}
case .rightToLeft:
return {
return $0.frame.maxX > $1.frame.maxX
}
case .radial(let center):
return {
return $0.center.distance(center) < $1.center.distance(center)
}
case .inverseRadial(let center):
return {
return $0.center.distance(center) > $1.center.distance(center)
}
}
/// Based on the cascade direction a comparator is set.
var comparator: (UIView, UIView) -> Bool {
switch self {
case .topToBottom:
return {
return $0.frame.minY < $1.frame.minY
}
case .bottomToTop:
return {
return $0.frame.maxY == $1.frame.maxY ? $0.frame.maxX > $1.frame.maxX : $0.frame.maxY > $1.frame.maxY
}
case .leftToRight:
return {
return $0.frame.minX < $1.frame.minX
}
case .rightToLeft:
return {
return $0.frame.maxX > $1.frame.maxX
}
case .radial(let center):
return {
return $0.center.distance(center) < $1.center.distance(center)
}
case .inverseRadial(let center):
return {
return $0.center.distance(center) > $1.center.distance(center)
}
}
}
}
class CascadePreprocessor: MotionCorePreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews)
process(views: toViews)
}
/**
Process an Array of views for the cascade animation.
- Parameter views: An Array of UIViews.
*/
func process(views: [UIView]) {
for v in views {
guard let (deltaTime, direction, delayMatchedViews) = context[v]?.cascade else {
continue
}
var parentView = v
if v is UITableView, let wrapperView = v.subviews.get(0) {
parentView = wrapperView
}
let sortedSubviews = parentView.subviews.sorted(by: direction.comparator)
let initialDelay = context[v]!.delay
let finalDelay = TimeInterval(sortedSubviews.count) * deltaTime + initialDelay
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews)
process(views: toViews)
}
/**
Process an Array of views for the cascade animation.
- Parameter views: An Array of UIViews.
*/
func process(views: [UIView]) {
for v in views {
guard let (deltaTime, direction, delayMatchedViews) = context[v]?.cascade else {
continue
}
var parentView = v
if v is UITableView, let wrapperView = v.subviews.get(0) {
parentView = wrapperView
}
let sortedSubviews = parentView.subviews.sorted(by: direction.comparator)
let initialDelay = context[v]!.delay
let finalDelay = TimeInterval(sortedSubviews.count) * deltaTime + initialDelay
for (i, subview) in sortedSubviews.enumerated() {
let delay = TimeInterval(i) * deltaTime + initialDelay
func applyDelay(view: UIView) {
if nil == context.pairedView(for: view) {
context[view]?.delay = delay
for (i, subview) in sortedSubviews.enumerated() {
let delay = TimeInterval(i) * deltaTime + initialDelay
func applyDelay(view: UIView) {
if nil == context.pairedView(for: view) {
context[view]?.delay = delay
} else if delayMatchedViews, let paired = context.pairedView(for: view) {
context[view]?.delay = finalDelay
context[paired]?.delay = finalDelay
}
for subview in view.subviews {
applyDelay(view: subview)
}
}
applyDelay(view: subview)
}
} else if delayMatchedViews, let paired = context.pairedView(for: view) {
context[view]?.delay = finalDelay
context[paired]?.delay = finalDelay
}
for subview in view.subviews {
applyDelay(view: subview)
}
}
applyDelay(view: subview)
}
}
}
}
......@@ -29,84 +29,84 @@
import UIKit
public struct MotionConditionalContext {
internal weak var motion: MotionTransition!
public weak var view: UIView!
internal weak var motion: MotionTransition!
public weak var view: UIView!
public private(set) var isAppearing: Bool
public var isPresenting: Bool {
return motion.isPresenting
}
public var isTabBarController: Bool {
return motion.isTabBarController
}
public var isNavigationController: Bool {
return motion.isNavigationController
}
public var isMatched: Bool {
return nil != matchedView
}
public var isAncestorViewMatched: Bool {
return nil != matchedAncestorView
}
public var matchedView: UIView? {
return motion.context.pairedView(for: view)
}
public var matchedAncestorView: (UIView, UIView)? {
var current = view.superview
public private(set) var isAppearing: Bool
public var isPresenting: Bool {
return motion.isPresenting
}
public var isTabBarController: Bool {
return motion.isTabBarController
}
public var isNavigationController: Bool {
return motion.isNavigationController
}
public var isMatched: Bool {
return nil != matchedView
}
public var isAncestorViewMatched: Bool {
return nil != matchedAncestorView
while let ancestor = current, ancestor != motion.context.container {
if let pairedView = motion.context.pairedView(for: ancestor) {
return (ancestor, pairedView)
}
current = ancestor.superview
}
public var matchedView: UIView? {
return motion.context.pairedView(for: view)
}
public var matchedAncestorView: (UIView, UIView)? {
var current = view.superview
while let ancestor = current, ancestor != motion.context.container {
if let pairedView = motion.context.pairedView(for: ancestor) {
return (ancestor, pairedView)
}
current = ancestor.superview
}
return nil
}
public var fromViewController: UIViewController {
return motion.fromViewController!
}
public var toViewController: UIViewController {
return motion.toViewController!
}
public var currentViewController: UIViewController {
return isAppearing ? toViewController : fromViewController
}
public var otherViewController: UIViewController {
return isAppearing ? fromViewController : toViewController
}
return nil
}
public var fromViewController: UIViewController {
return motion.fromViewController!
}
public var toViewController: UIViewController {
return motion.toViewController!
}
public var currentViewController: UIViewController {
return isAppearing ? toViewController : fromViewController
}
public var otherViewController: UIViewController {
return isAppearing ? fromViewController : toViewController
}
}
class ConditionalPreprocessor: MotionCorePreprocessor {
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews, isAppearing: false)
process(views: toViews, isAppearing: true)
}
func process(views: [UIView], isAppearing: Bool) {
for v in views {
guard let conditionalModifiers = context[v]?.conditionalModifiers else {
continue
}
for (condition, modifiers) in conditionalModifiers {
if condition(MotionConditionalContext(motion: motion, view: v, isAppearing: isAppearing)) {
context[v]!.append(contentsOf: modifiers)
}
}
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews, isAppearing: false)
process(views: toViews, isAppearing: true)
}
func process(views: [UIView], isAppearing: Bool) {
for v in views {
guard let conditionalModifiers = context[v]?.conditionalModifiers else {
continue
}
for (condition, modifiers) in conditionalModifiers {
if condition(MotionConditionalContext(motion: motion, view: v, isAppearing: isAppearing)) {
context[v]!.append(contentsOf: modifiers)
}
}
}
}
}
......@@ -29,54 +29,54 @@
import UIKit
class IgnoreSubviewTransitionsPreprocessor: MotionCorePreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews)
process(views: toViews)
}
/**
Process an Array of views for the cascade animation.
- Parameter views: An Array of UIViews.
*/
func process(views: [UIView]) {
for v in views {
guard let recursive = context[v]?.ignoreSubviewTransitions else {
continue
}
var parentView = v
if v is UITableView, let wrapperView = v.subviews.get(0) {
parentView = wrapperView
}
guard recursive else {
for subview in parentView.subviews {
context[subview] = nil
}
continue
}
cleanSubviewModifiers(for: parentView)
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
process(views: fromViews)
process(views: toViews)
}
/**
Process an Array of views for the cascade animation.
- Parameter views: An Array of UIViews.
*/
func process(views: [UIView]) {
for v in views {
guard let recursive = context[v]?.ignoreSubviewTransitions else {
continue
}
var parentView = v
if v is UITableView, let wrapperView = v.subviews.get(0) {
parentView = wrapperView
}
guard recursive else {
for subview in parentView.subviews {
context[subview] = nil
}
continue
}
cleanSubviewModifiers(for: parentView)
}
}
}
fileprivate extension IgnoreSubviewTransitionsPreprocessor {
/**
Clears the modifiers for a given view's subviews.
- Parameter for view: A UIView.
*/
func cleanSubviewModifiers(for view: UIView) {
for v in view.subviews {
context[v] = nil
cleanSubviewModifiers(for: v)
}
/**
Clears the modifiers for a given view's subviews.
- Parameter for view: A UIView.
*/
func cleanSubviewModifiers(for view: UIView) {
for v in view.subviews {
context[v] = nil
cleanSubviewModifiers(for: v)
}
}
}
......@@ -29,66 +29,66 @@
import UIKit
class MatchPreprocessor: MotionCorePreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews {
guard let motionIdentifier = tv.motionIdentifier, let fv = context.sourceView(for: motionIdentifier) else {
continue
}
var tvState = context[tv] ?? MotionTargetState()
var fvState = context[fv] ?? MotionTargetState()
// match is just a two-way source effect
tvState.motionIdentifier = motionIdentifier
fvState.motionIdentifier = motionIdentifier
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
fvState.delay = tvState.delay
fvState.spring = tvState.spring
let forceNonFade = tvState.nonFade || fvState.nonFade
let isNonOpaque = !fv.isOpaque || fv.alpha < 1 || !tv.isOpaque || tv.alpha < 1
if context.insertToViewFirst {
fvState.opacity = 0
if !forceNonFade && isNonOpaque {
tvState.opacity = 0
} else {
tvState.opacity = nil
if !tv.layer.masksToBounds && tvState.displayShadow {
fvState.displayShadow = false
}
}
} else {
tvState.opacity = 0
if !forceNonFade && isNonOpaque {
// cross fade if from/toViews are not opaque
fvState.opacity = 0
} else {
// no cross fade in this case, fromView is always displayed during the transition.
fvState.opacity = nil
// we dont want two shadows showing up. Therefore we disable toView's shadow when fromView is able to display its shadow
if !fv.layer.masksToBounds && fvState.displayShadow {
tvState.displayShadow = false
}
}
}
context[tv] = tvState
context[fv] = fvState
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
for tv in toViews {
guard let motionIdentifier = tv.motionIdentifier, let fv = context.sourceView(for: motionIdentifier) else {
continue
}
var tvState = context[tv] ?? MotionTargetState()
var fvState = context[fv] ?? MotionTargetState()
// match is just a two-way source effect
tvState.motionIdentifier = motionIdentifier
fvState.motionIdentifier = motionIdentifier
fvState.arc = tvState.arc
fvState.duration = tvState.duration
fvState.timingFunction = tvState.timingFunction
fvState.delay = tvState.delay
fvState.spring = tvState.spring
let forceNonFade = tvState.nonFade || fvState.nonFade
let isNonOpaque = !fv.isOpaque || fv.alpha < 1 || !tv.isOpaque || tv.alpha < 1
if context.insertToViewFirst {
fvState.opacity = 0
if !forceNonFade && isNonOpaque {
tvState.opacity = 0
} else {
tvState.opacity = nil
if !tv.layer.masksToBounds && tvState.displayShadow {
fvState.displayShadow = false
}
}
} else {
tvState.opacity = 0
if !forceNonFade && isNonOpaque {
// cross fade if from/toViews are not opaque
fvState.opacity = 0
} else {
// no cross fade in this case, fromView is always displayed during the transition.
fvState.opacity = nil
// we dont want two shadows showing up. Therefore we disable toView's shadow when fromView is able to display its shadow
if !fv.layer.masksToBounds && fvState.displayShadow {
tvState.displayShadow = false
}
}
}
context[tv] = tvState
context[fv] = fvState
}
}
}
......@@ -29,13 +29,13 @@
import UIKit
class MotionCorePreprocessor: MotionPreprocessor {
weak public var motion: MotionTransition!
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion!.context
}
func process(fromViews: [UIView], toViews: [UIView]) {}
weak public var motion: MotionTransition!
/// A reference to the MotionContext.
public var context: MotionContext! {
return motion!.context
}
func process(fromViews: [UIView], toViews: [UIView]) {}
}
......@@ -29,13 +29,13 @@
import UIKit
public protocol MotionPreprocessor: class {
/// A reference to Motion.
weak var motion: MotionTransition! { get set }
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
func process(fromViews: [UIView], toViews: [UIView])
/// A reference to Motion.
weak var motion: MotionTransition! { get set }
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
func process(fromViews: [UIView], toViews: [UIView])
}
......@@ -29,90 +29,90 @@
import UIKit
class SourcePreprocessor: MotionCorePreprocessor {
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
for fv in fromViews {
guard let motionIdentifier = context[fv]?.motionIdentifier, let tv = context.destinationView(for: motionIdentifier) else {
continue
}
prepare(view: fv, for: tv)
}
for tv in toViews {
guard let motionIdentifier = context[tv]?.motionIdentifier, let fv = context.sourceView(for: motionIdentifier) else {
continue
}
prepare(view: tv, for: fv)
}
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override func process(fromViews: [UIView], toViews: [UIView]) {
for fv in fromViews {
guard let motionIdentifier = context[fv]?.motionIdentifier, let tv = context.destinationView(for: motionIdentifier) else {
continue
}
prepare(view: fv, for: tv)
}
for tv in toViews {
guard let motionIdentifier = context[tv]?.motionIdentifier, let fv = context.sourceView(for: motionIdentifier) else {
continue
}
prepare(view: tv, for: fv)
}
}
}
fileprivate extension SourcePreprocessor {
/**
Prepares a given view for a target view.
- Parameter view: A UIView.
- Parameter for targetView: A UIView.
*/
func prepare(view: UIView, for targetView: UIView) {
let targetPos = context.container.convert(targetView.layer.position, from: targetView.superview!)
let targetTransform = context.container.layer.flatTransformTo(layer: targetView.layer)
var state = context[view]!
/**
Prepares a given view for a target view.
- Parameter view: A UIView.
- Parameter for targetView: A UIView.
Use global coordinate space since over target position is
converted from the global container
*/
func prepare(view: UIView, for targetView: UIView) {
let targetPos = context.container.convert(targetView.layer.position, from: targetView.superview!)
let targetTransform = context.container.layer.flatTransformTo(layer: targetView.layer)
var state = context[view]!
/**
Use global coordinate space since over target position is
converted from the global container
*/
state.coordinateSpace = .global
state.position = targetPos
state.transform = targetTransform
// Remove incompatible options.
state.size = 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.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[view] = state
state.coordinateSpace = .global
state.position = targetPos
state.transform = targetTransform
// Remove incompatible options.
state.size = 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.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[view] = state
}
}
......@@ -29,55 +29,55 @@
import UIKit
protocol MotionProgressRunnerDelegate: class {
func update(progress: TimeInterval)
func complete(isFinishing: Bool)
func update(progress: TimeInterval)
func complete(isFinishing: Bool)
}
class MotionProgressRunner {
weak var delegate: MotionProgressRunnerDelegate?
weak var delegate: MotionProgressRunnerDelegate?
var isRunning: Bool {
return nil != displayLink
}
internal var progress: TimeInterval = 0
internal var duration: TimeInterval = 0
internal var displayLink: CADisplayLink?
internal var isReversed: Bool = false
@objc
func displayUpdate(_ link: CADisplayLink) {
progress += isReversed ? -link.duration : link.duration
var isRunning: Bool {
return nil != displayLink
if isReversed, progress <= 1.0 / 120 {
delegate?.complete(isFinishing: false)
stop()
return
}
internal var progress: TimeInterval = 0
internal var duration: TimeInterval = 0
internal var displayLink: CADisplayLink?
internal var isReversed: Bool = false
@objc
func displayUpdate(_ link: CADisplayLink) {
progress += isReversed ? -link.duration : link.duration
if isReversed, progress <= 1.0 / 120 {
delegate?.complete(isFinishing: false)
stop()
return
}
if !isReversed, progress > duration - 1.0 / 120 {
delegate?.complete(isFinishing: true)
stop()
return
}
delegate?.update(progress: progress / duration)
if !isReversed, progress > duration - 1.0 / 120 {
delegate?.complete(isFinishing: true)
stop()
return
}
func start(progress: TimeInterval, duration: TimeInterval, isReversed: Bool) {
stop()
self.progress = progress
self.isReversed = isReversed
self.duration = duration
displayLink = CADisplayLink(target: self, selector: #selector(displayUpdate(_:)))
displayLink!.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
delegate?.update(progress: progress / duration)
}
func start(progress: TimeInterval, duration: TimeInterval, isReversed: Bool) {
stop()
func stop() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
}
self.progress = progress
self.isReversed = isReversed
self.duration = duration
displayLink = CADisplayLink(target: self, selector: #selector(displayUpdate(_:)))
displayLink!.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
func stop() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
}
}
......@@ -29,79 +29,79 @@
import UIKit
extension MotionTransition {
/// Starts the transition animation.
func animate() {
guard .starting == state else {
return
}
state = .animating
if let tv = toView {
context.unhide(view: tv)
}
for v in animatingFromViews {
context.hide(view: v)
}
for v in animatingToViews {
context.hide(view: v)
}
var t: TimeInterval = 0
var animatorWantsInteractive = false
if context.insertToViewFirst {
for v in animatingToViews {
context.snapshotView(for: v)
}
for v in animatingFromViews {
context.snapshotView(for: v)
}
} else {
for v in animatingFromViews {
context.snapshotView(for: v)
}
for v in animatingToViews {
context.snapshotView(for: v)
}
}
// UIKit appears to set fromView setNeedLayout to be true.
// We don't want fromView to layout after our animation starts.
// Therefore we kick off the layout beforehand
fromView?.layoutIfNeeded()
for animator in animators {
let duration = animator.animate(fromViews: animatingFromViews.filter {
return animator.canAnimate(view: $0, isAppearing: false)
}, toViews: animatingToViews.filter {
return animator.canAnimate(view: $0, isAppearing: true)
})
if .infinity == duration {
animatorWantsInteractive = true
} else {
t = max(t, duration)
}
}
totalDuration = t
if let forceFinishing = forceFinishing {
complete(isFinishing: forceFinishing)
} else if let startingProgress = startingProgress {
update(startingProgress)
} else if animatorWantsInteractive {
update(0)
} else {
complete(after: totalDuration, isFinishing: true)
}
fullScreenSnapshot?.removeFromSuperview()
/// Starts the transition animation.
func animate() {
guard .starting == state else {
return
}
state = .animating
if let tv = toView {
context.unhide(view: tv)
}
for v in animatingFromViews {
context.hide(view: v)
}
for v in animatingToViews {
context.hide(view: v)
}
var t: TimeInterval = 0
var animatorWantsInteractive = false
if context.insertToViewFirst {
for v in animatingToViews {
context.snapshotView(for: v)
}
for v in animatingFromViews {
context.snapshotView(for: v)
}
} else {
for v in animatingFromViews {
context.snapshotView(for: v)
}
for v in animatingToViews {
context.snapshotView(for: v)
}
}
// UIKit appears to set fromView setNeedLayout to be true.
// We don't want fromView to layout after our animation starts.
// Therefore we kick off the layout beforehand
fromView?.layoutIfNeeded()
for animator in animators {
let duration = animator.animate(fromViews: animatingFromViews.filter {
return animator.canAnimate(view: $0, isAppearing: false)
}, toViews: animatingToViews.filter {
return animator.canAnimate(view: $0, isAppearing: true)
})
if .infinity == duration {
animatorWantsInteractive = true
} else {
t = max(t, duration)
}
}
totalDuration = t
if let forceFinishing = forceFinishing {
complete(isFinishing: forceFinishing)
} else if let startingProgress = startingProgress {
update(startingProgress)
} else if animatorWantsInteractive {
update(0)
} else {
complete(after: totalDuration, isFinishing: true)
}
fullScreenSnapshot?.removeFromSuperview()
}
}
......@@ -29,119 +29,119 @@
import UIKit
extension MotionTransition {
/**
Complete the transition.
- Parameter isFinishing: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinishing: Bool) {
if state == .notified {
forceFinishing = isFinishing
}
guard .animating == state || .starting == state else {
return
}
defer {
transitionContext = nil
fromViewController = nil
toViewController = nil
isNavigationController = false
isTabBarController = false
forceNonInteractive = false
animatingToViews.removeAll()
animatingFromViews.removeAll()
transitionObservers = nil
transitionContainer = nil
completionCallback = nil
forceFinishing = nil
container = nil
startingProgress = nil
preprocessors.removeAll()
animators.removeAll()
plugins.removeAll()
context = nil
progress = 0
totalDuration = 0
state = .possible
}
state = .completing
progressRunner.stop()
context.clean()
if let tv = toView, let fv = fromView {
if isFinishing && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen view controller.
context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv)
context.storeViewAlpha(rootView: fv)
fromViewController!.motionStoredSnapshot = container
fv.removeFromSuperview()
fv.addSubview(container)
} else if !isFinishing && !isPresenting && fromOverFullScreen {
// Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv)
context.storeViewAlpha(rootView: tv)
toViewController!.motionStoredSnapshot = container
container.superview?.addSubview(tv)
tv.addSubview(container)
} else {
context.unhideAll()
context.removeAllSnapshots()
}
// Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinishing) || (fromOverFullScreen && !isFinishing) {
transitionContainer?.addSubview(isFinishing ? fv : tv)
}
transitionContainer?.addSubview(isFinishing ? tv : fv)
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)
}
}
if container.superview == transitionContainer {
container.removeFromSuperview()
}
for a in animators {
a.clean()
}
transitionContainer?.isUserInteractionEnabled = true
completionCallback?(isFinishing)
/**
Complete the transition.
- Parameter isFinishing: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinishing: Bool) {
if state == .notified {
forceFinishing = isFinishing
}
guard .animating == state || .starting == state else {
return
}
defer {
transitionContext = nil
fromViewController = nil
toViewController = nil
isNavigationController = false
isTabBarController = false
forceNonInteractive = false
animatingToViews.removeAll()
animatingFromViews.removeAll()
transitionObservers = nil
transitionContainer = nil
completionCallback = nil
forceFinishing = nil
container = nil
startingProgress = nil
preprocessors.removeAll()
animators.removeAll()
plugins.removeAll()
context = nil
progress = 0
totalDuration = 0
state = .possible
}
state = .completing
progressRunner.stop()
context.clean()
if let tv = toView, let fv = fromView {
if isFinishing && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen view controller.
context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv)
context.storeViewAlpha(rootView: fv)
if isFinishing {
toViewController?.tabBarController?.tabBar.layer.removeAllAnimations()
} else {
fromViewController?.tabBarController?.tabBar.layer.removeAllAnimations()
}
fromViewController!.motionStoredSnapshot = container
fv.removeFromSuperview()
fv.addSubview(container)
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
} else if !isFinishing && !isPresenting && fromOverFullScreen {
// Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv)
context.storeViewAlpha(rootView: tv)
if isFinishing {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
tContext?.cancelInteractiveTransition()
}
toViewController!.motionStoredSnapshot = container
container.superview?.addSubview(tv)
tv.addSubview(container)
tContext?.completeTransition(isFinishing)
} else {
context.unhideAll()
context.removeAllSnapshots()
}
// Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinishing) || (fromOverFullScreen && !isFinishing) {
transitionContainer?.addSubview(isFinishing ? fv : tv)
}
transitionContainer?.addSubview(isFinishing ? tv : fv)
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)
}
}
if container.superview == transitionContainer {
container.removeFromSuperview()
}
for a in animators {
a.clean()
}
transitionContainer?.isUserInteractionEnabled = true
completionCallback?(isFinishing)
if isFinishing {
toViewController?.tabBarController?.tabBar.layer.removeAllAnimations()
} else {
fromViewController?.tabBarController?.tabBar.layer.removeAllAnimations()
}
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
if isFinishing {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
tContext?.cancelInteractiveTransition()
}
tContext?.completeTransition(isFinishing)
}
}
......@@ -29,34 +29,34 @@
import UIKit
extension MotionTransition {
/**
A helper transition function.
- Parameter from: A UIViewController.
- Parameter to: A UIViewController.
- Parameter in view: A UIView.
- Parameter completion: An optional completion handler.
*/
public func transition(from: UIViewController, to: UIViewController, in view: UIView, completion: ((Bool) -> Void)? = nil) {
guard !isTransitioning else {
return
}
state = .notified
isPresenting = true
transitionContainer = view
fromViewController = from
toViewController = to
completionCallback = { [weak self] in
guard let `self` = self else {
return
}
completion?($0)
self.state = .possible
}
start()
/**
A helper transition function.
- Parameter from: A UIViewController.
- Parameter to: A UIViewController.
- Parameter in view: A UIView.
- Parameter completion: An optional completion handler.
*/
public func transition(from: UIViewController, to: UIViewController, in view: UIView, completion: ((Bool) -> Void)? = nil) {
guard !isTransitioning else {
return
}
state = .notified
isPresenting = true
transitionContainer = view
fromViewController = from
toViewController = to
completionCallback = { [weak self] in
guard let `self` = self else {
return
}
completion?($0)
self.state = .possible
}
start()
}
}
......@@ -29,100 +29,100 @@
import UIKit
extension MotionTransition {
/**
Updates the elapsed time for the interactive transition.
- Parameter progress t: the current progress, must be between -1...1.
*/
func update(_ percentageComplete: TimeInterval) {
guard .animating == state else {
startingProgress = percentageComplete
return
}
progressRunner.stop()
progress = Double(CGFloat(percentageComplete).clamp(0, 1))
/**
Updates the elapsed time for the interactive transition.
- Parameter progress t: the current progress, must be between -1...1.
*/
func update(_ percentageComplete: TimeInterval) {
guard .animating == state else {
startingProgress = percentageComplete
return
}
/**
Finish the interactive transition.
Will stop the interactive transition and animate from the
current state to the **end** state.
- Parameter isAnimated: A Boolean.
*/
func finish(isAnimated: Bool = true) {
guard .animating == state || .notified == state || .starting == state else {
return
}
guard isAnimated else {
complete(isFinishing: true)
return
}
var d: TimeInterval = 0
for a in animators {
d = max(d, a.resume(at: progress * totalDuration, isReversed: false))
}
complete(after: d, isFinishing: true)
progressRunner.stop()
progress = Double(CGFloat(percentageComplete).clamp(0, 1))
}
/**
Finish the interactive transition.
Will stop the interactive transition and animate from the
current state to the **end** state.
- Parameter isAnimated: A Boolean.
*/
func finish(isAnimated: Bool = true) {
guard .animating == state || .notified == state || .starting == state else {
return
}
/**
Cancel the interactive transition.
Will stop the interactive transition and animate from the
current state to the **begining** state
- Parameter isAnimated: A boolean indicating if the completion is animated.
*/
func cancel(isAnimated: Bool = true) {
guard .animating == state || .notified == state || .starting == state else {
return
}
guard isAnimated else {
complete(isFinishing: false)
return
}
var d: TimeInterval = 0
for a in animators {
var t = progress
if t < 0 {
t = -t
}
d = max(d, a.resume(at: t * totalDuration, isReversed: true))
}
complete(after: d, isFinishing: false)
guard isAnimated else {
complete(isFinishing: true)
return
}
/**
Override transition animations during an interactive animation.
For example:
Motion.shared.apply([.position(x:50, y:50)], to: view)
will set the view's position to 50, 50
- Parameter modifiers: An Array of MotionModifier.
- Parameter to view: A UIView.
*/
func apply(modifiers: [MotionModifier], to view: UIView) {
guard .animating == state else {
return
}
let targetState = MotionTargetState(modifiers: modifiers)
if let otherView = context.pairedView(for: view) {
for animator in animators {
animator.apply(state: targetState, to: otherView)
}
}
for animator in self.animators {
animator.apply(state: targetState, to: view)
}
var d: TimeInterval = 0
for a in animators {
d = max(d, a.resume(at: progress * totalDuration, isReversed: false))
}
complete(after: d, isFinishing: true)
}
/**
Cancel the interactive transition.
Will stop the interactive transition and animate from the
current state to the **begining** state
- Parameter isAnimated: A boolean indicating if the completion is animated.
*/
func cancel(isAnimated: Bool = true) {
guard .animating == state || .notified == state || .starting == state else {
return
}
guard isAnimated else {
complete(isFinishing: false)
return
}
var d: TimeInterval = 0
for a in animators {
var t = progress
if t < 0 {
t = -t
}
d = max(d, a.resume(at: t * totalDuration, isReversed: true))
}
complete(after: d, isFinishing: false)
}
/**
Override transition animations during an interactive animation.
For example:
Motion.shared.apply([.position(x:50, y:50)], to: view)
will set the view's position to 50, 50
- Parameter modifiers: An Array of MotionModifier.
- Parameter to view: A UIView.
*/
func apply(modifiers: [MotionModifier], to view: UIView) {
guard .animating == state else {
return
}
let targetState = MotionTargetState(modifiers: modifiers)
if let otherView = context.pairedView(for: view) {
for animator in animators {
animator.apply(state: targetState, to: otherView)
}
}
for animator in self.animators {
animator.apply(state: targetState, to: view)
}
}
}
......@@ -29,21 +29,21 @@
import UIKit
extension MotionTransition: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
state = .notified
isPresenting = .push == operation
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isNavigationController = true
return self
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
state = .notified
isPresenting = .push == operation
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isNavigationController = true
return self
}
public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
}
......@@ -29,34 +29,34 @@
import UIKit
extension MotionTransition: UITabBarControllerDelegate {
public func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if isTransitioning {
cancel(isAnimated: false)
}
return true
public func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if isTransitioning {
cancel(isAnimated: false)
}
public func tabBarController(_ tabBarController: UITabBarController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
return true
}
public func tabBarController(_ tabBarController: UITabBarController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
public func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
public func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
state = .notified
let fromVCIndex = tabBarController.childViewControllers.index(of: fromVC)!
let toVCIndex = tabBarController.childViewControllers.index(of: toVC)!
isPresenting = toVCIndex > fromVCIndex
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isTabBarController = true
return self
}
state = .notified
let fromVCIndex = tabBarController.childViewControllers.index(of: fromVC)!
let toVCIndex = tabBarController.childViewControllers.index(of: toVC)!
isPresenting = toVCIndex > fromVCIndex
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isTabBarController = true
return self
}
}
......@@ -29,78 +29,78 @@
import UIKit
extension MotionTransition: UIViewControllerTransitioningDelegate {
/// A reference to the interactive transitioning instance.
var interactiveTransitioning: UIViewControllerInteractiveTransitioning? {
return forceNonInteractive ? nil : self
/// A reference to the interactive transitioning instance.
var interactiveTransitioning: UIViewControllerInteractiveTransitioning? {
return forceNonInteractive ? nil : self
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
state = .notified
isPresenting = true
fromViewController = fromViewController ?? presenting
toViewController = toViewController ?? presented
return self
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
state = .notified
isPresenting = false
fromViewController = fromViewController ?? dismissed
return self
}
state = .notified
isPresenting = true
fromViewController = fromViewController ?? presenting
toViewController = toViewController ?? presented
public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
return self
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard !isTransitioning else {
return nil
}
public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
state = .notified
isPresenting = false
fromViewController = fromViewController ?? dismissed
return self
}
public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactiveTransitioning
}
}
extension MotionTransition: UIViewControllerAnimatedTransitioning {
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
public func animateTransition(using context: UIViewControllerContextTransitioning) {
transitionContext = context
fromViewController = fromViewController ?? context.viewController(forKey: .from)
toViewController = toViewController ?? context.viewController(forKey: .to)
transitionContainer = context.containerView
start()
}
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
public func animateTransition(using context: UIViewControllerContextTransitioning) {
transitionContext = context
fromViewController = fromViewController ?? context.viewController(forKey: .from)
toViewController = toViewController ?? context.viewController(forKey: .to)
transitionContainer = context.containerView
/**
Returns the transition duration time interval.
- Parameter using transitionContext: An optional UIViewControllerContextTransitioning.
- Returns: A TimeInterval that is the total animation time including delays.
*/
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0 // Time will be updated dynamically.
}
public func animationEnded(_ transitionCompleted: Bool) {
state = .possible
}
start()
}
/**
Returns the transition duration time interval.
- Parameter using transitionContext: An optional UIViewControllerContextTransitioning.
- Returns: A TimeInterval that is the total animation time including delays.
*/
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0 // Time will be updated dynamically.
}
public func animationEnded(_ transitionCompleted: Bool) {
state = .possible
}
}
extension MotionTransition: UIViewControllerInteractiveTransitioning {
public var wantsInteractiveStart: Bool {
return true
}
public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
animateTransition(using: transitionContext)
}
public var wantsInteractiveStart: Bool {
return true
}
public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
animateTransition(using: transitionContext)
}
}
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