Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
Motion
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
Motion
Commits
fcf3d822
Unverified
Commit
fcf3d822
authored
Jun 08, 2017
by
Daniel Dahan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
initial pass at MotionController rework
parent
19664a2f
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
281 additions
and
146 deletions
+281
-146
Motion.xcodeproj/project.pbxproj
+8
-4
Sources/Motion.swift
+87
-51
Sources/MotionController.swift
+148
-87
Sources/MotionTransitionObserver.swift
+38
-0
Sources/MotionViewController.swift
+0
-4
No files found.
Motion.xcodeproj/project.pbxproj
View file @
fcf3d822
...
...
@@ -37,7 +37,7 @@
96AEB6A31EE4610F009A3BE0
/* MotionPlugin.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB67D1EE4610F009A3BE0
/* MotionPlugin.swift */
;
};
96AEB6A41EE4610F009A3BE0
/* MotionStringConvertible.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB67E1EE4610F009A3BE0
/* MotionStringConvertible.swift */
;
};
96AEB6A51EE4610F009A3BE0
/* MotionTargetState.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB67F1EE4610F009A3BE0
/* MotionTargetState.swift */
;
};
96AEB6A61EE4610F009A3BE0
/* Motion
Types.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB6801EE4610F009A3BE0
/* MotionTypes
.swift */
;
};
96AEB6A61EE4610F009A3BE0
/* Motion
ViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB6801EE4610F009A3BE0
/* MotionViewController
.swift */
;
};
96AEB6A71EE4610F009A3BE0
/* Lexer.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB6821EE4610F009A3BE0
/* Lexer.swift */
;
};
96AEB6A81EE4610F009A3BE0
/* Nodes.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB6831EE4610F009A3BE0
/* Nodes.swift */
;
};
96AEB6A91EE4610F009A3BE0
/* Parser.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB6841EE4610F009A3BE0
/* Parser.swift */
;
};
...
...
@@ -48,6 +48,7 @@
96AEB6AE1EE4610F009A3BE0
/* IgnoreSubviewModifiersPreprocessor.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB68A1EE4610F009A3BE0
/* IgnoreSubviewModifiersPreprocessor.swift */
;
};
96AEB6AF1EE4610F009A3BE0
/* MatchPreprocessor.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB68B1EE4610F009A3BE0
/* MatchPreprocessor.swift */
;
};
96AEB6B01EE4610F009A3BE0
/* SourcePreprocessor.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96AEB68C1EE4610F009A3BE0
/* SourcePreprocessor.swift */
;
};
96E49A401EEA08F8006D5A93
/* MotionTransitionObserver.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E49A3F1EEA08F8006D5A93
/* MotionTransitionObserver.swift */
;
};
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
...
...
@@ -80,7 +81,7 @@
96AEB67D1EE4610F009A3BE0
/* MotionPlugin.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionPlugin.swift
;
sourceTree
=
"<group>"
;
};
96AEB67E1EE4610F009A3BE0
/* MotionStringConvertible.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionStringConvertible.swift
;
sourceTree
=
"<group>"
;
};
96AEB67F1EE4610F009A3BE0
/* MotionTargetState.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionTargetState.swift
;
sourceTree
=
"<group>"
;
};
96AEB6801EE4610F009A3BE0
/* Motion
Types.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionTypes
.swift
;
sourceTree
=
"<group>"
;
};
96AEB6801EE4610F009A3BE0
/* Motion
ViewController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionViewController
.swift
;
sourceTree
=
"<group>"
;
};
96AEB6821EE4610F009A3BE0
/* Lexer.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Lexer.swift
;
sourceTree
=
"<group>"
;
};
96AEB6831EE4610F009A3BE0
/* Nodes.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Nodes.swift
;
sourceTree
=
"<group>"
;
};
96AEB6841EE4610F009A3BE0
/* Parser.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Parser.swift
;
sourceTree
=
"<group>"
;
};
...
...
@@ -95,6 +96,7 @@
96C98DDD1E424B9000B22906
/* Info.plist */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
text.plist.xml
;
path
=
Info.plist
;
sourceTree
=
"<group>"
;
};
96C98DE21E43809D00B22906
/* LICENSE */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
text
;
path
=
LICENSE
;
sourceTree
=
"<group>"
;
};
96C98DED1E438A5700B22906
/* Motion.h */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.c.h
;
path
=
Motion.h
;
sourceTree
=
"<group>"
;
};
96E49A3F1EEA08F8006D5A93
/* MotionTransitionObserver.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionTransitionObserver.swift
;
sourceTree
=
"<group>"
;
};
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
...
...
@@ -207,7 +209,8 @@
96AEB67D1EE4610F009A3BE0
/* MotionPlugin.swift */
,
96AEB67E1EE4610F009A3BE0
/* MotionStringConvertible.swift */
,
96AEB67F1EE4610F009A3BE0
/* MotionTargetState.swift */
,
96AEB6801EE4610F009A3BE0
/* MotionTypes.swift */
,
96AEB6801EE4610F009A3BE0
/* MotionViewController.swift */
,
96E49A3F1EEA08F8006D5A93
/* MotionTransitionObserver.swift */
,
96AEB6811EE4610F009A3BE0
/* Parser */
,
96AEB6861EE4610F009A3BE0
/* Preprocessors */
,
);
...
...
@@ -308,9 +311,10 @@
96AEB6961EE4610F009A3BE0
/* Motion+CAMediaTimingFunction.swift in Sources */
,
96AEB6941EE4610F009A3BE0
/* Motion+Array.swift in Sources */
,
96AEB6951EE4610F009A3BE0
/* Motion+CALayer.swift in Sources */
,
96AEB6A61EE4610F009A3BE0
/* Motion
Types
.swift in Sources */
,
96AEB6A61EE4610F009A3BE0
/* Motion
ViewController
.swift in Sources */
,
96AEB68E1EE4610F009A3BE0
/* MotionCoreAnimationViewContext.swift in Sources */
,
96AEB6921EE4610F009A3BE0
/* MotionDebugView.swift in Sources */
,
96E49A401EEA08F8006D5A93
/* MotionTransitionObserver.swift in Sources */
,
96AEB6A01EE4610F009A3BE0
/* MotionIndependentController.swift in Sources */
,
96AEB6AA1EE4610F009A3BE0
/* Regex.swift in Sources */
,
96AEB6901EE4610F009A3BE0
/* MotionViewPropertyViewContext.swift in Sources */
,
...
...
Sources/Motion.swift
View file @
fcf3d822
...
...
@@ -58,11 +58,13 @@ public class Motion: MotionController {
public
internal(set)
var
isPresenting
=
true
/// Progress of the current transition, 0 if a transition is not happening.
public
override
var
progress
:
Double
{
public
override
var
elapsedTime
:
TimeInterval
{
didSet
{
if
isTransitioning
{
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
progress
))
guard
isTransitioning
else
{
return
}
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
elapsedTime
))
}
}
...
...
@@ -155,7 +157,7 @@ public extension Motion {
}
}
internal
extension
Motion
{
fileprivate
extension
Motion
{
/// Starts the transition animation.
func
start
()
{
guard
isTransitioning
else
{
...
...
@@ -165,51 +167,25 @@ internal extension Motion {
prepareViewControllers
()
prepareSnapshotView
()
prepareForTransition
()
prepare
Motion
Context
()
prepareContext
()
prepareToView
()
prepareViewHierarchy
()
processContext
()
prepareForAnimation
()
context
.
hide
(
view
:
toView
)
#if os(tvOS)
animate
()
#else
if
isNavigationController
{
// When animating within navigationController, we have to dispatch later into the main queue.
// otherwise snapshots will be pure white. Possibly a bug with UIKit
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
self
?
.
animate
()
}
}
else
{
animate
()
}
#endif
processForAnimation
()
}
}
internal
extension
Motion
{
override
func
animate
()
{
context
.
unhide
(
view
:
toView
)
if
let
containerBackgroundColor
=
containerBackgroundColor
{
container
.
backgroundColor
=
containerBackgroundColor
}
else
if
!
toOverFullScreen
&&
!
fromOverFullScreen
{
container
.
backgroundColor
=
toView
.
backgroundColor
}
if
fromOverFullScreen
{
insertToViewFirst
=
true
}
for
animator
in
animators
{
if
let
animator
=
animator
as?
MotionHasInsertOrder
{
animator
.
insertToViewFirst
=
insertToViewFirst
}
}
updateContainerBackgroundColor
()
updateInsertOrder
()
super
.
animate
()
fullScreenSnapshot
!.
removeFromSuperview
()
fullScreenSnapshot
?
.
removeFromSuperview
()
}
override
func
complete
(
finished
:
Bool
)
{
...
...
@@ -344,7 +320,7 @@ fileprivate extension Motion {
}
/// Prepares the MotionContext instance.
func
prepare
Motion
Context
()
{
func
prepareContext
()
{
context
.
loadViewAlpha
(
rootView
:
toView
)
context
.
loadViewAlpha
(
rootView
:
fromView
)
container
.
addSubview
(
toView
)
...
...
@@ -370,6 +346,52 @@ internal extension Motion {
super
.
prepareForTransition
()
insert
(
preprocessor
:
DefaultAnimationPreprocessor
(
motion
:
self
),
before
:
DurationPreprocessor
.
self
)
}
override
func
prepareForAnimation
()
{
super
.
prepareForAnimation
()
context
.
hide
(
view
:
toView
)
}
}
fileprivate
extension
Motion
{
/// Processes the animations.
func
processForAnimation
()
{
#if os(tvOS)
animate
()
#else
if
isNavigationController
{
// When animating within navigationController, we have to dispatch later into the main queue.
// otherwise snapshots will be pure white. Possibly a bug with UIKit
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
self
?
.
animate
()
}
}
else
{
animate
()
}
#endif
}
}
fileprivate
extension
Motion
{
/// Updates the container background color.
func
updateContainerBackgroundColor
()
{
if
let
v
=
containerBackgroundColor
{
container
.
backgroundColor
=
v
}
else
if
!
toOverFullScreen
&&
!
fromOverFullScreen
{
container
.
backgroundColor
=
toView
.
backgroundColor
}
}
/// Updates the insertToViewFirst boolean for animators.
func
updateInsertOrder
()
{
if
fromOverFullScreen
{
insertToViewFirst
=
true
}
for
v
in
animators
{
(
v
as?
MotionHasInsertOrder
)?
.
insertToViewFirst
=
insertToViewFirst
}
}
}
internal
extension
Motion
{
...
...
@@ -418,23 +440,31 @@ internal extension Motion {
}
}
// MARK: UIKit Protocol Conformance
/*****************************
* UIKit protocol extensions *
*****************************/
extension
Motion
:
UIViewControllerAnimatedTransitioning
{
/**
The animation method that is used to coordinate the transition.
- Parameter using transitionContext: A UIViewControllerContextTransitioning.
*/
public
func
animateTransition
(
using
context
:
UIViewControllerContextTransitioning
)
{
guard
!
isTransitioning
else
{
return
}
guard
!
isTransitioning
else
{
return
}
transitionContext
=
context
fromViewController
=
fromViewController
??
context
.
viewController
(
forKey
:
.
from
)
toViewController
=
toViewController
??
context
.
viewController
(
forKey
:
.
to
)
transitionContainer
=
context
.
containerView
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.375
// doesn't matter, real duration will be calculated later
return
0
// Will be updated dynamically.
}
public
func
animationEnded
(
_
transitionCompleted
:
Bool
)
{
...
...
@@ -442,11 +472,14 @@ extension Motion: UIViewControllerAnimatedTransitioning {
}
}
extension
Motion
:
UIViewControllerTransitioningDelegate
{
var
interactiveTransitioning
:
UIViewControllerInteractiveTransitioning
?
{
return
forceNonInteractive
?
nil
:
self
extension
Motion
{
/// A reference to the interactive transitioning.
fileprivate
var
interactiveTransitioning
:
UIViewControllerInteractiveTransitioning
?
{
return
self
}
}
extension
Motion
:
UIViewControllerTransitioningDelegate
{
public
func
animationController
(
forPresented
presented
:
UIViewController
,
presenting
:
UIViewController
,
source
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
self
.
isPresenting
=
true
self
.
fromViewController
=
fromViewController
??
presenting
...
...
@@ -480,7 +513,7 @@ extension Motion: UIViewControllerInteractiveTransitioning {
extension
Motion
:
UINavigationControllerDelegate
{
public
func
navigationController
(
_
navigationController
:
UINavigationController
,
animationControllerFor
operation
:
UINavigationControllerOperation
,
from
fromVC
:
UIViewController
,
to
toVC
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
self
.
isPresenting
=
operation
==
.
push
self
.
isPresenting
=
.
push
==
operation
self
.
fromViewController
=
fromViewController
??
fromVC
self
.
toViewController
=
toViewController
??
toVC
self
.
isNavigationController
=
true
...
...
@@ -503,12 +536,15 @@ extension Motion: UITabBarControllerDelegate {
public
func
tabBarController
(
_
tabBarController
:
UITabBarController
,
animationControllerForTransitionFrom
fromVC
:
UIViewController
,
to
toVC
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
isAnimating
=
true
let
fromVCIndex
=
tabBarController
.
childViewControllers
.
index
(
of
:
fromVC
)
!
let
toVCIndex
=
tabBarController
.
childViewControllers
.
index
(
of
:
toVC
)
!
self
.
isPresenting
=
toVCIndex
>
fromVCIndex
self
.
fromViewController
=
fromViewController
??
fromVC
self
.
toViewController
=
toViewController
??
toVC
self
.
isTabBarController
=
true
return
self
}
}
...
...
Sources/MotionController.swift
View file @
fcf3d822
...
...
@@ -28,107 +28,168 @@
import
UIKit
/// Base class for managing a Motion transition
public
class
MotionController
:
NSObject
{
// MARK: Properties
/// context object holding transition informations
/// A reference to the MotionContext.
public
internal(set)
var
context
:
MotionContext
!
/// whether or not we are handling transition interactively
public
var
interactive
:
Bool
{
return
displayLink
==
nil
/// A boolean indicating whether the transition interactive or not.
public
var
isInteractive
:
Bool
{
return
nil
==
displayLink
}
/// progress of the current transition. 0 if no transition is happening
public
internal(set)
var
progress
:
Double
=
0
{
/// Progress of the current transition. 0 if no transition is happening.
public
internal(set)
var
elapsedTime
:
TimeInterval
=
0
{
didSet
{
if
isTransitioning
{
if
let
progressUpdateObservers
=
progressUpdateObservers
{
for
observer
in
progressUpdateObservers
{
observer
.
motionDidUpdateProgress
(
progress
:
progress
)
}
guard
isTransitioning
else
{
return
}
let
elapsedTime
=
progress
*
totalDuration
if
interactive
{
for
animator
in
animators
{
animator
.
seek
(
to
:
elapsedTime
)
}
}
else
{
for
plugin
in
plugins
where
plugin
.
requirePerFrameCallback
{
plugin
.
seek
(
to
:
elapsedTime
)
}
}
updateTransitionObservers
()
guard
isInteractive
else
{
updatePlugins
()
return
}
updateAnimators
()
}
}
/// whether or not we are doing a transition
/// A boolean indicating whether a transition is active.
public
var
isTransitioning
:
Bool
{
return
transitionContainer
!=
nil
return
nil
!=
transitionContainer
}
/// container we created to hold all animating views, will be a subview of the
/// transitionContainer when isTransitioning
/**
A view container used to hold all the animating views during a
transition.
*/
public
internal(set)
var
container
:
UIView
!
/// this is the container supplied by UIKit
/// UIKit's supplied transition container.
internal
var
transitionContainer
:
UIView
!
/// An optional completion callback.
internal
var
completionCallback
:
((
Bool
)
->
Void
)?
/// Binds the render cycle to the transition animation.
internal
var
displayLink
:
CADisplayLink
?
internal
var
progressUpdateObservers
:
[
MotionProgressUpdateObserver
]?
/// max duration needed by the default animator and plugins
public
internal(set)
var
totalDuration
:
TimeInterval
=
0.0
/// An Array of observers that are updated during a transition.
internal
var
transitionObservers
:
[
MotionTransitionObserver
]?
/// Max duration used by MotionAnimators and MotionPlugins.
public
internal(set)
var
totalDuration
:
TimeInterval
=
0
/// current animation complete duration.
/// (differs from totalDuration because this one could be the duration for finishing interactive transition)
internal
var
duration
:
TimeInterval
=
0.0
/// The currently running animation duration.
internal
var
currentAnimationDuration
:
TimeInterval
=
0
/// The start time of the animation.
internal
var
beginTime
:
TimeInterval
?
{
didSet
{
if
beginTime
!=
nil
{
if
displayLink
==
nil
{
displayLink
=
CADisplayLink
(
target
:
self
,
selector
:
#selector(
displayUpdate(_:)
)
)
displayLink
!.
add
(
to
:
RunLoop
.
main
,
forMode
:
RunLoopMode
(
rawValue
:
RunLoopMode
.
commonModes
.
rawValue
))
}
}
else
{
guard
nil
!=
beginTime
else
{
displayLink
?
.
isPaused
=
true
displayLink
?
.
remove
(
from
:
RunLoop
.
main
,
forMode
:
RunLoopMode
(
rawValue
:
RunLoopMode
.
commonModes
.
rawValue
))
displayLink
=
nil
return
}
}
}
func
displayUpdate
(
_
link
:
CADisplayLink
)
{
if
isTransitioning
,
duration
>
0
,
let
beginTime
=
beginTime
{
let
elapsedTime
=
CACurrentMediaTime
()
-
beginTime
if
elapsedTime
>
duration
{
progress
=
finishing
?
1
:
0
self
.
beginTime
=
nil
complete
(
finished
:
finishing
)
}
else
{
var
completed
=
elapsedTime
/
totalDuration
if
!
finishing
{
completed
=
1
-
completed
}
completed
=
max
(
0
,
min
(
1
,
completed
))
progress
=
completed
guard
nil
==
displayLink
else
{
return
}
displayLink
=
CADisplayLink
(
target
:
self
,
selector
:
#selector(
handleDisplayLink(_:)
)
)
displayLink
?
.
add
(
to
:
RunLoop
.
main
,
forMode
:
RunLoopMode
(
rawValue
:
RunLoopMode
.
commonModes
.
rawValue
))
}
}
internal
var
finishing
:
Bool
=
true
/// A boolean indicating if the transition has finished.
internal
var
isFinished
=
true
/// An Array of MotionPreprocessors used during a transition.
internal
var
processors
:
[
MotionPreprocessor
]
!
/// An Array of MotionAnimators used during a transition.
internal
var
animators
:
[
MotionAnimator
]
!
/// An Array of MotionPlugins used during a transition.
internal
var
plugins
:
[
MotionPlugin
]
!
internal
var
animatingViews
:
[(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])]
!
/// The matching from-views to to-views based on the motionIdentifier value.
internal
var
transitionPairs
:
[(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])]
!
internal
static
var
enabledPlugins
:
[
MotionPlugin
.
Type
]
=
[]
/// Plugins that are enabled during the transition.
internal
static
var
enabledPlugins
=
[
MotionPlugin
.
Type
]()
/// Initializer.
internal
override
init
()
{}
}
fileprivate
extension
MotionController
{
/// Updates the transition observers.
func
updateTransitionObservers
()
{
guard
let
observers
=
transitionObservers
else
{
return
}
for
v
in
observers
{
v
.
motion
(
transitionObserver
:
v
,
didUpdateWith
:
elapsedTime
)
}
}
/// Updates the animators.
func
updateAnimators
()
{
let
v
=
elapsedTime
*
totalDuration
for
a
in
animators
{
a
.
seek
(
to
:
v
)
}
}
/// Updates the plugins.
func
updatePlugins
()
{
let
v
=
elapsedTime
*
totalDuration
for
p
in
plugins
where
p
.
requirePerFrameCallback
{
p
.
seek
(
to
:
v
)
}
}
}
fileprivate
extension
MotionController
{
@objc
func
handleDisplayLink
(
_
link
:
CADisplayLink
)
{
guard
isTransitioning
else
{
return
}
guard
0
<
currentAnimationDuration
else
{
return
}
guard
let
t
=
beginTime
else
{
return
}
let
cTime
=
CACurrentMediaTime
()
-
t
if
cTime
>
currentAnimationDuration
{
elapsedTime
=
isFinished
?
1
:
0
beginTime
=
nil
complete
(
isFinished
:
isFinished
)
}
else
{
var
eTime
=
cTime
/
totalDuration
if
!
isFinished
{
eTime
=
1
-
eTime
}
elapsedTime
=
max
(
0
,
min
(
1
,
eTime
))
}
}
}
public
extension
MotionController
{
// MARK: Interactive Transition
...
...
@@ -140,7 +201,7 @@ public extension MotionController {
public
func
update
(
progress
:
Double
)
{
guard
isTransitioning
else
{
return
}
self
.
beginTime
=
nil
self
.
progress
=
max
(
-
1
,
min
(
1
,
progress
))
self
.
elapsedTime
=
max
(
-
1
,
min
(
1
,
progress
))
}
/**
...
...
@@ -151,14 +212,14 @@ public extension MotionController {
public
func
end
(
animate
:
Bool
=
true
)
{
guard
isTransitioning
else
{
return
}
if
!
animate
{
self
.
complete
(
f
inished
:
true
)
self
.
complete
(
isF
inished
:
true
)
return
}
var
maxTime
:
TimeInterval
=
0
for
animator
in
self
.
animators
{
maxTime
=
max
(
maxTime
,
animator
.
resume
(
at
:
self
.
progress
*
self
.
totalDuration
,
isReversed
:
false
))
maxTime
=
max
(
maxTime
,
animator
.
resume
(
at
:
self
.
elapsedTime
*
self
.
totalDuration
,
isReversed
:
false
))
}
self
.
complete
(
after
:
maxTime
,
finishing
:
true
)
self
.
complete
(
after
:
maxTime
,
isFinished
:
true
)
}
/**
...
...
@@ -169,18 +230,18 @@ public extension MotionController {
public
func
cancel
(
animate
:
Bool
=
true
)
{
guard
isTransitioning
else
{
return
}
if
!
animate
{
self
.
complete
(
f
inished
:
false
)
self
.
complete
(
isF
inished
:
false
)
return
}
var
maxTime
:
TimeInterval
=
0
for
animator
in
self
.
animators
{
var
adjustedProgress
=
self
.
progress
var
adjustedProgress
=
self
.
elapsedTime
if
adjustedProgress
<
0
{
adjustedProgress
=
-
adjustedProgress
}
maxTime
=
max
(
maxTime
,
animator
.
resume
(
at
:
adjustedProgress
*
self
.
totalDuration
,
isReversed
:
true
))
}
self
.
complete
(
after
:
maxTime
,
finishing
:
false
)
self
.
complete
(
after
:
maxTime
,
isFinished
:
false
)
}
/**
...
...
@@ -219,11 +280,11 @@ public extension MotionController {
- Parameters:
- observer: the observer
*/
func
observeForProgressUpdate
(
observer
:
Motion
ProgressUpdate
Observer
)
{
if
progressUpdate
Observers
==
nil
{
progressUpdate
Observers
=
[]
func
observeForProgressUpdate
(
observer
:
Motion
Transition
Observer
)
{
if
transition
Observers
==
nil
{
transition
Observers
=
[]
}
progressUpdate
Observers
!.
append
(
observer
)
transition
Observers
!.
append
(
observer
)
}
}
...
...
@@ -283,7 +344,7 @@ internal extension MotionController {
func
prepareForAnimation
()
{
guard
isTransitioning
else
{
fatalError
()
}
animatingView
s
=
[([
UIView
],
[
UIView
])]()
transitionPair
s
=
[([
UIView
],
[
UIView
])]()
for
animator
in
animators
{
let
currentFromViews
=
context
.
fromViews
.
filter
{
(
view
:
UIView
)
->
Bool
in
return
animator
.
canAnimate
(
view
:
view
,
isAppearing
:
false
)
...
...
@@ -291,7 +352,7 @@ internal extension MotionController {
let
currentToViews
=
context
.
toViews
.
filter
{
(
view
:
UIView
)
->
Bool
in
return
animator
.
canAnimate
(
view
:
view
,
isAppearing
:
true
)
}
animatingView
s
.
append
((
currentFromViews
,
currentToViews
))
transitionPair
s
.
append
((
currentFromViews
,
currentToViews
))
}
}
...
...
@@ -299,7 +360,7 @@ internal extension MotionController {
/// subclass should call `prepareForTransition` & `prepareForAnimation` before calling `animate`
func
animate
()
{
guard
isTransitioning
else
{
fatalError
()
}
for
(
currentFromViews
,
currentToViews
)
in
animatingView
s
{
for
(
currentFromViews
,
currentToViews
)
in
transitionPair
s
{
// auto hide all animated views
for
view
in
currentFromViews
{
context
.
hide
(
view
:
view
)
...
...
@@ -312,8 +373,8 @@ internal extension MotionController {
var
totalDuration
:
TimeInterval
=
0
var
animatorWantsInteractive
=
false
for
(
i
,
animator
)
in
animators
.
enumerated
()
{
let
duration
=
animator
.
animate
(
fromViews
:
animatingView
s
[
i
]
.
0
,
toViews
:
animatingView
s
[
i
]
.
1
)
let
duration
=
animator
.
animate
(
fromViews
:
transitionPair
s
[
i
]
.
0
,
toViews
:
transitionPair
s
[
i
]
.
1
)
if
duration
==
.
infinity
{
animatorWantsInteractive
=
true
}
else
{
...
...
@@ -325,23 +386,23 @@ internal extension MotionController {
if
animatorWantsInteractive
{
update
(
progress
:
0
)
}
else
{
complete
(
after
:
totalDuration
,
finishing
:
true
)
complete
(
after
:
totalDuration
,
isFinished
:
true
)
}
}
func
complete
(
after
:
TimeInterval
,
finishing
:
Bool
)
{
func
complete
(
after
:
TimeInterval
,
isFinished
:
Bool
)
{
guard
isTransitioning
else
{
fatalError
()
}
if
after
<=
0.001
{
complete
(
finished
:
finishing
)
complete
(
isFinished
:
isFinished
)
return
}
let
elapsedTime
=
(
finishing
?
progress
:
1
-
progress
)
*
totalDuration
self
.
finishing
=
finishing
self
.
duration
=
after
+
elapsedTime
self
.
beginTime
=
CACurrentMediaTime
()
-
elapsedTime
let
v
=
(
isFinished
?
elapsedTime
:
1
-
elapsedTime
)
*
totalDuration
self
.
isFinished
=
isFinished
self
.
currentAnimationDuration
=
after
+
v
self
.
beginTime
=
CACurrentMediaTime
()
-
v
}
func
complete
(
f
inished
:
Bool
)
{
func
complete
(
isF
inished
:
Bool
)
{
guard
isTransitioning
else
{
fatalError
()
}
for
animator
in
animators
{
animator
.
clean
()
...
...
@@ -351,8 +412,8 @@ internal extension MotionController {
let
completion
=
completionCallback
animatingView
s
=
nil
progressUpdate
Observers
=
nil
transitionPair
s
=
nil
transition
Observers
=
nil
transitionContainer
=
nil
completionCallback
=
nil
container
=
nil
...
...
@@ -361,10 +422,10 @@ internal extension MotionController {
plugins
=
nil
context
=
nil
beginTime
=
nil
progress
=
0
elapsedTime
=
0
totalDuration
=
0
completion
?(
f
inished
)
completion
?(
isF
inished
)
}
}
...
...
Sources/MotionTransitionObserver.swift
0 → 100644
View file @
fcf3d822
/*
* 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
Foundation
public
protocol
MotionTransitionObserver
{
/**
Executed when the elapsed time changes during a transition.
- Parameter transitionObserver: A MotionTransitionObserver.
- Parameter didUpdateWith elapsedTime: A TimeInterval.
*/
func
motion
(
transitionObserver
:
MotionTransitionObserver
,
didUpdateWith
elapsedTime
:
TimeInterval
)
}
Sources/Motion
Types
.swift
→
Sources/Motion
ViewController
.swift
View file @
fcf3d822
...
...
@@ -28,10 +28,6 @@
import
UIKit
public
protocol
MotionProgressUpdateObserver
{
func
motionDidUpdateProgress
(
progress
:
Double
)
}
@objc(MotionViewControllerDelegate)
public
protocol
MotionViewControllerDelegate
{
@objc
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment