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
104aead0
Commit
104aead0
authored
Dec 15, 2017
by
Daniel Dahan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
updated MotionTransition extensions before full review
parent
150344a1
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
290 additions
and
211 deletions
+290
-211
Motion.xcodeproj/project.pbxproj
+8
-0
Sources/Animator/MotionAnimator.swift
+4
-4
Sources/Animator/MotionAnimatorViewContext.swift
+4
-4
Sources/Animator/MotionCoreAnimationViewContext.swift
+11
-11
Sources/Animator/MotionCoreAnimator.swift
+2
-2
Sources/Animator/MotionTransitionAnimator.swift
+7
-7
Sources/Animator/MotionViewPropertyViewContext.swift
+3
-3
Sources/MotionPlugin.swift
+4
-4
Sources/MotionProgressRunner.swift
+2
-2
Sources/MotionTransitionObserver.swift
+2
-2
Sources/Transition/MotionTransition+Animate.swift
+1
-4
Sources/Transition/MotionTransition+Complete.swift
+3
-3
Sources/Transition/MotionTransition+CustomTransition.swift
+62
-0
Sources/Transition/MotionTransition+Interactive.swift
+128
-0
Sources/Transition/MotionTransition+Start.swift
+41
-59
Sources/Transition/MotionTransition.swift
+8
-106
No files found.
Motion.xcodeproj/project.pbxproj
View file @
104aead0
...
...
@@ -28,6 +28,8 @@
965FE98D1FE334E10098BDD0
/* MotionTransition+UIViewControllerTransitioningDelegate.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965FE98C1FE334E10098BDD0
/* MotionTransition+UIViewControllerTransitioningDelegate.swift */
;
};
965FE9911FE43DA60098BDD0
/* MotionTransition+UITabBarControllerDelegate.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965FE9901FE43DA60098BDD0
/* MotionTransition+UITabBarControllerDelegate.swift */
;
};
965FE9931FE43DE10098BDD0
/* MotionTransition+UINavigationControllerDelegate.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965FE9921FE43DE10098BDD0
/* MotionTransition+UINavigationControllerDelegate.swift */
;
};
965FE9A11FE43EF80098BDD0
/* MotionTransition+CustomTransition.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965FE9A01FE43EF80098BDD0
/* MotionTransition+CustomTransition.swift */
;
};
965FE9A31FE4407D0098BDD0
/* MotionTransition+Interactive.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965FE9A21FE4407D0098BDD0
/* MotionTransition+Interactive.swift */
;
};
96E409651F24F7370015A2B5
/* MotionAnimator.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E4093D1F24F7370015A2B5
/* MotionAnimator.swift */
;
};
96E409661F24F7370015A2B5
/* MotionAnimatorViewContext.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E4093E1F24F7370015A2B5
/* MotionAnimatorViewContext.swift */
;
};
96E409671F24F7370015A2B5
/* MotionCoreAnimationViewContext.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96E4093F1F24F7370015A2B5
/* MotionCoreAnimationViewContext.swift */
;
};
...
...
@@ -104,6 +106,8 @@
965FE98C1FE334E10098BDD0
/* MotionTransition+UIViewControllerTransitioningDelegate.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"MotionTransition+UIViewControllerTransitioningDelegate.swift"
;
sourceTree
=
"<group>"
;
};
965FE9901FE43DA60098BDD0
/* MotionTransition+UITabBarControllerDelegate.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"MotionTransition+UITabBarControllerDelegate.swift"
;
sourceTree
=
"<group>"
;
};
965FE9921FE43DE10098BDD0
/* MotionTransition+UINavigationControllerDelegate.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"MotionTransition+UINavigationControllerDelegate.swift"
;
sourceTree
=
"<group>"
;
};
965FE9A01FE43EF80098BDD0
/* MotionTransition+CustomTransition.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"MotionTransition+CustomTransition.swift"
;
sourceTree
=
"<group>"
;
};
965FE9A21FE4407D0098BDD0
/* MotionTransition+Interactive.swift */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
sourcecode.swift
;
path
=
"MotionTransition+Interactive.swift"
;
sourceTree
=
"<group>"
;
};
96C98DD11E424AB000B22906
/* Motion.framework */
=
{
isa
=
PBXFileReference
;
explicitFileType
=
wrapper.framework
;
includeInIndex
=
0
;
path
=
Motion.framework
;
sourceTree
=
BUILT_PRODUCTS_DIR
;
};
96E4093D1F24F7370015A2B5
/* MotionAnimator.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionAnimator.swift
;
sourceTree
=
"<group>"
;
};
96E4093E1F24F7370015A2B5
/* MotionAnimatorViewContext.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
MotionAnimatorViewContext.swift
;
sourceTree
=
"<group>"
;
};
...
...
@@ -151,6 +155,8 @@
965FE98C1FE334E10098BDD0
/* MotionTransition+UIViewControllerTransitioningDelegate.swift */
,
965FE9901FE43DA60098BDD0
/* MotionTransition+UITabBarControllerDelegate.swift */
,
965FE9921FE43DE10098BDD0
/* MotionTransition+UINavigationControllerDelegate.swift */
,
965FE9A01FE43EF80098BDD0
/* MotionTransition+CustomTransition.swift */
,
965FE9A21FE4407D0098BDD0
/* MotionTransition+Interactive.swift */
,
);
path
=
Transition
;
sourceTree
=
"<group>"
;
...
...
@@ -371,6 +377,7 @@
96E409841F24F7370015A2B5
/* IgnoreSubviewModifiersPreprocessor.swift in Sources */
,
96E409771F24F7370015A2B5
/* MotionAnimationState.swift in Sources */
,
96E409761F24F7370015A2B5
/* MotionAnimation.swift in Sources */
,
965FE9A31FE4407D0098BDD0
/* MotionTransition+Interactive.swift in Sources */
,
96E4096B1F24F7370015A2B5
/* Motion+Array.swift in Sources */
,
96E409721F24F7370015A2B5
/* Motion+UIViewController.swift in Sources */
,
96E4097E1F24F7370015A2B5
/* MotionSnapshotType.swift in Sources */
,
...
...
@@ -378,6 +385,7 @@
965FE97A1FE1D83D0098BDD0
/* MotionTransition.swift in Sources */
,
965FE98D1FE334E10098BDD0
/* MotionTransition+UIViewControllerTransitioningDelegate.swift in Sources */
,
96E409871F24F7370015A2B5
/* SourcePreprocessor.swift in Sources */
,
965FE9A11FE43EF80098BDD0
/* MotionTransition+CustomTransition.swift in Sources */
,
96E409701F24F7370015A2B5
/* Motion+UIKit.swift in Sources */
,
965FE9771FE0976F0098BDD0
/* ConditionalPreprocessor.swift in Sources */
,
96E4097B1F24F7370015A2B5
/* MotionCoordinateSpace.swift in Sources */
,
...
...
Sources/Animator/MotionAnimator.swift
View file @
104aead0
...
...
@@ -53,18 +53,18 @@ public protocol MotionAnimator: class {
/**
Moves the view's animation to the given elapsed time.
- Parameter to
elapsedTime
: A TimeInterval.
- Parameter to
progress
: A TimeInterval.
*/
func
seek
(
to
elapsedTime
:
TimeInterval
)
func
seek
(
to
progress
:
TimeInterval
)
/**
Resumes the animation with a given elapsed time and
optional reversed boolean.
- Parameter at
elapsedTime
: A TimeInterval.
- Parameter at
progress
: A TimeInterval.
- Parameter isReversed: A boolean to reverse the animation
or not.
*/
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
/**
Applies the given state to the given view.
...
...
Sources/Animator/MotionAnimatorViewContext.swift
View file @
104aead0
...
...
@@ -88,21 +88,21 @@ internal class MotionAnimatorViewContext {
/**
Resumes the animation with a given elapsed time and
optional reversed boolean.
- Parameter at
elapsedTime
: A TimeInterval.
- Parameter at
progress
: A TimeInterval.
- Parameter isReversed: A boolean to reverse the animation
or not.
- Returns: A TimeInterval.
*/
@discardableResult
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
return
0
}
/**
Moves the animation to the given elapsed time.
- Parameter to
elapsedTime
: A TimeInterval.
- Parameter to
progress
: A TimeInterval.
*/
func
seek
(
to
elapsedTime
:
TimeInterval
)
{}
func
seek
(
to
progress
:
TimeInterval
)
{}
/**
Applies the given state to the target state.
...
...
Sources/Animator/MotionCoreAnimationViewContext.swift
View file @
104aead0
...
...
@@ -86,26 +86,26 @@ internal class MotionCoreAnimationViewContext: MotionAnimatorViewContext {
}
}
override
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
override
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
for
(
key
,
(
fromValue
,
toValue
))
in
state
{
state
[
key
]
=
(
currentValue
(
for
:
key
),
isReversed
?
fromValue
:
toValue
)
}
if
isReversed
{
if
elapsedTime
>
targetState
.
delay
+
duration
{
let
backDelay
=
elapsedTime
-
(
targetState
.
delay
+
duration
)
if
progress
>
targetState
.
delay
+
duration
{
let
backDelay
=
progress
-
(
targetState
.
delay
+
duration
)
return
animate
(
delay
:
backDelay
,
duration
:
duration
)
}
else
if
elapsedTime
>
targetState
.
delay
{
return
animate
(
delay
:
0
,
duration
:
duration
-
elapsedTime
-
targetState
.
delay
)
}
else
if
progress
>
targetState
.
delay
{
return
animate
(
delay
:
0
,
duration
:
duration
-
progress
-
targetState
.
delay
)
}
}
else
{
if
elapsedTime
<=
targetState
.
delay
{
return
animate
(
delay
:
targetState
.
delay
-
elapsedTime
,
duration
:
duration
)
if
progress
<=
targetState
.
delay
{
return
animate
(
delay
:
targetState
.
delay
-
progress
,
duration
:
duration
)
}
else
if
elapsedTime
<=
targetState
.
delay
+
duration
{
let
timePassedDelay
=
elapsedTime
-
targetState
.
delay
}
else
if
progress
<=
targetState
.
delay
+
duration
{
let
timePassedDelay
=
progress
-
targetState
.
delay
return
animate
(
delay
:
0
,
duration
:
duration
-
timePassedDelay
)
}
}
...
...
@@ -113,8 +113,8 @@ internal class MotionCoreAnimationViewContext: MotionAnimatorViewContext {
return
0
}
override
func
seek
(
to
elapsedTime
:
TimeInterval
)
{
let
timeOffset
=
CGFloat
(
elapsedTime
-
targetState
.
delay
)
override
func
seek
(
to
progress
:
TimeInterval
)
{
let
timeOffset
=
CGFloat
(
progress
-
targetState
.
delay
)
for
(
layer
,
key
,
anim
)
in
animations
{
anim
.
speed
=
0
...
...
Sources/Animator/MotionCoreAnimator.swift
View file @
104aead0
...
...
@@ -46,9 +46,9 @@ class MotionCoreAnimator: MotionAnimator {
return
0
}
func
seek
(
to
elapsedTime
:
TimeInterval
)
{}
func
seek
(
to
progress
:
TimeInterval
)
{}
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
return
0
}
...
...
Sources/Animator/MotionTransitionAnimator.swift
View file @
104aead0
...
...
@@ -101,30 +101,30 @@ internal class MotionTargetStateAnimator<T: MotionAnimatorViewContext>: MotionCo
/**
Moves the view's animation to the given elapsed time.
- Parameter to
elapsedTime
: A TimeInterval.
- Parameter to
progress
: A TimeInterval.
*/
override
func
seek
(
to
elapsedTime
:
TimeInterval
)
{
override
func
seek
(
to
progress
:
TimeInterval
)
{
for
v
in
viewToContexts
.
values
{
v
.
seek
(
to
:
elapsedTime
)
v
.
seek
(
to
:
progress
)
}
}
/**
Resumes the animation with a given elapsed time and
optional reversed boolean.
- Parameter at
elapsedTime
: A TimeInterval.
- Parameter at
progress
: A TimeInterval.
- Parameter isReversed: A boolean to reverse the animation
or not.
*/
override
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
override
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
var
duration
:
TimeInterval
=
0
for
(
_
,
v
)
in
viewToContexts
{
if
nil
==
v
.
targetState
.
duration
{
v
.
duration
=
max
(
v
.
duration
,
v
.
snapshot
.
optimizedDuration
(
targetState
:
v
.
targetState
)
+
elapsedTime
)
v
.
duration
=
max
(
v
.
duration
,
v
.
snapshot
.
optimizedDuration
(
targetState
:
v
.
targetState
)
+
progress
)
}
duration
=
max
(
duration
,
v
.
resume
(
at
:
elapsedTime
,
isReversed
:
isReversed
))
duration
=
max
(
duration
,
v
.
resume
(
at
:
progress
,
isReversed
:
isReversed
))
}
return
duration
...
...
Sources/Animator/MotionViewPropertyViewContext.swift
View file @
104aead0
...
...
@@ -43,7 +43,7 @@ internal class MotionViewPropertyViewContext: MotionAnimatorViewContext {
return
view
is
UIVisualEffectView
&&
nil
!=
state
.
opacity
}
override
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
override
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
guard
let
visualEffectView
=
snapshot
as?
UIVisualEffectView
else
{
return
0
}
...
...
@@ -65,9 +65,9 @@ internal class MotionViewPropertyViewContext: MotionAnimatorViewContext {
return
duration
}
override
func
seek
(
to
elapsedTime
:
TimeInterval
)
{
override
func
seek
(
to
progress
:
TimeInterval
)
{
viewPropertyAnimator
?
.
pauseAnimation
()
viewPropertyAnimator
?
.
fractionComplete
=
CGFloat
(
elapsedTime
/
duration
)
viewPropertyAnimator
?
.
fractionComplete
=
CGFloat
(
progress
/
duration
)
}
override
func
clean
()
{
...
...
Sources/MotionPlugin.swift
View file @
104aead0
...
...
@@ -101,9 +101,9 @@ class MotionPlugin: MotionCorePreprocessor, MotionAnimator {
This method is called when an interactive animation is in place
The plugin should pause the animation, and seek to the given progress
- Parameters:
-
elapsedTime
: time of the animation to seek to.
-
progress
: time of the animation to seek to.
*/
open
func
seek
(
to
elapsedTime
:
TimeInterval
)
{}
open
func
seek
(
to
progress
:
TimeInterval
)
{}
/**
For supporting interactive animation only.
...
...
@@ -111,10 +111,10 @@ class MotionPlugin: MotionCorePreprocessor, MotionAnimator {
This method is called when an interactive animation is ended
The plugin should resume the animation.
- Parameters:
-
elapsedTime
: will be the same value since last `seekTo`
-
progress
: will be the same value since last `seekTo`
- reverse: a boolean value indicating whether or not the animation should reverse
*/
open
func
resume
(
at
elapsedTime
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
return
0
}
open
func
resume
(
at
progress
:
TimeInterval
,
isReversed
:
Bool
)
->
TimeInterval
{
return
0
}
/**
For supporting interactive animation only.
...
...
Sources/MotionProgressRunner.swift
View file @
104aead0
...
...
@@ -29,7 +29,7 @@
import
UIKit
protocol
MotionProgressRunnerDelegate
:
class
{
func
update
(
elapsedTime
:
TimeInterval
)
func
update
(
progress
:
TimeInterval
)
func
complete
(
isFinishing
:
Bool
)
}
...
...
@@ -61,7 +61,7 @@ class MotionProgressRunner {
return
}
delegate
?
.
update
(
elapsedTime
:
timePassed
/
duration
)
delegate
?
.
update
(
progress
:
timePassed
/
duration
)
}
func
start
(
timePassed
:
TimeInterval
,
totalTime
:
TimeInterval
,
reverse
:
Bool
)
{
...
...
Sources/MotionTransitionObserver.swift
View file @
104aead0
...
...
@@ -32,7 +32,7 @@ public protocol MotionTargetStateObserver {
/**
Executed when the elapsed time changes during a transition.
- Parameter transitionObserver: A MotionTargetStateObserver.
- Parameter didUpdateWith
elapsedTime
: A TimeInterval.
- Parameter didUpdateWith
progress
: A TimeInterval.
*/
func
motion
(
transitionObserver
:
MotionTargetStateObserver
,
didUpdateWith
elapsedTime
:
TimeInterval
)
func
motion
(
transitionObserver
:
MotionTargetStateObserver
,
didUpdateWith
progress
:
TimeInterval
)
}
Sources/Transition/MotionTransition+Animate.swift
View file @
104aead0
...
...
@@ -75,13 +75,10 @@ extension MotionTransition {
// 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
{
print
(
animator
.
canAnimate
(
view
:
$0
,
isAppearing
:
false
))
return
animator
.
canAnimate
(
view
:
$0
,
isAppearing
:
false
)
},
toViews
:
animatingToViews
.
filter
{
print
(
animator
.
canAnimate
(
view
:
$0
,
isAppearing
:
false
))
return
animator
.
canAnimate
(
view
:
$0
,
isAppearing
:
true
)
})
...
...
@@ -99,7 +96,7 @@ extension MotionTransition {
}
else
if
let
startingProgress
=
startingProgress
{
update
(
startingProgress
)
}
else
if
animatorWantsInteractive
{
update
(
elapsedTime
:
0
)
update
(
0
)
}
else
{
complete
(
after
:
totalDuration
,
isFinishing
:
true
)
}
...
...
Sources/Transition/MotionTransition+Complete.swift
View file @
104aead0
...
...
@@ -45,9 +45,9 @@ extension MotionTransition {
return
}
let
totalTime
=
after
/
(
isFinishing
?
max
((
1
-
elapsedTime
),
0.01
)
:
max
(
elapsedTime
,
0.01
))
let
totalTime
=
after
/
(
isFinishing
?
max
((
1
-
progress
),
0.01
)
:
max
(
progress
,
0.01
))
progressRunner
.
start
(
timePassed
:
elapsedTime
*
totalTime
,
totalTime
:
totalTime
,
reverse
:
!
isFinishing
)
progressRunner
.
start
(
timePassed
:
progress
*
totalTime
,
totalTime
:
totalTime
,
reverse
:
!
isFinishing
)
}
/**
...
...
@@ -84,7 +84,7 @@ extension MotionTransition {
animators
.
removeAll
()
plugins
.
removeAll
()
context
=
nil
elapsedTime
=
0
progress
=
0
totalDuration
=
0
state
=
.
possible
}
...
...
Sources/Transition/MotionTransition+CustomTransition.swift
0 → 100644
View file @
104aead0
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import
UIKit
extension
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
()
}
}
Sources/Transition/MotionTransition+Interactive.swift
0 → 100644
View file @
104aead0
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import
UIKit
extension
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
))
}
/**
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
)
}
/**
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
)
}
}
}
Sources/Transition/MotionTransition+Start.swift
View file @
104aead0
...
...
@@ -40,12 +40,27 @@ extension MotionTransition {
prepareViewFrame
()
prepareViewControllers
()
prepareSnapshotView
()
preparePlugins
()
preparePreprocessors
()
prepareAnimators
()
preparePlugins
()
for
v
in
plugins
{
preprocessors
.
append
(
v
)
animators
.
append
(
v
)
}
prepareTransitionContainer
()
prepareContainer
()
prepareContext
()
for
v
in
preprocessors
{
v
.
motion
=
self
}
for
v
in
animators
{
v
.
motion
=
self
}
prepareViewHierarchy
()
prepareAnimatingViews
()
prepareToView
()
...
...
@@ -83,14 +98,10 @@ fileprivate extension MotionTransition {
/// Prepares the snapshot view, which hides any flashing that may occur.
func
prepareSnapshotView
()
{
guard
let
v
=
transitionContainer
else
{
return
}
fullScreenSnapshot
=
v
.
window
?
.
snapshotView
(
afterScreenUpdates
:
false
)
??
fromView
?
.
snapshotView
(
afterScreenUpdates
:
false
)
fullScreenSnapshot
=
transitionContainer
?
.
window
?
.
snapshotView
(
afterScreenUpdates
:
false
)
??
fromView
?
.
snapshotView
(
afterScreenUpdates
:
false
)
if
let
snapshot
=
fullScreenSnapshot
{
(
v
.
window
??
v
)?
.
addSubview
(
snapshot
)
(
transitionContainer
?
.
window
??
transitionContainer
)?
.
addSubview
(
snapshot
)
}
if
let
v
=
fromViewController
?
.
motionStoredSnapshot
{
...
...
@@ -104,60 +115,35 @@ fileprivate extension MotionTransition {
}
}
/// Prepares the plugins.
func
preparePlugins
()
{
plugins
=
MotionTransition
.
enabledPlugins
.
map
{
return
$0
.
init
()
}
}
/// Prepares the preprocessors.
func
preparePreprocessors
()
{
for
x
in
[
IgnoreSubviewTransitionsPreprocessor
(),
ConditionalPreprocessor
(),
TransitionPreprocessor
(),
MatchPreprocessor
(),
SourcePreprocessor
(),
CascadePreprocessor
()]
as
[
MotionPreprocessor
]
{
preprocessors
.
append
(
x
)
}
for
v
in
preprocessors
{
v
.
motion
=
self
}
preprocessors
=
[
IgnoreSubviewTransitionsPreprocessor
(),
ConditionalPreprocessor
(),
TransitionPreprocessor
(),
MatchPreprocessor
(),
SourcePreprocessor
(),
CascadePreprocessor
()]
}
/// Prepares the animators.
func
prepareAnimators
()
{
animators
.
append
(
MotionTargetStateAnimator
<
MotionCoreAnimationViewContext
>
())
animators
=
[
MotionTargetStateAnimator
<
MotionCoreAnimationViewContext
>
()]
if
#available(iOS 10, tvOS 10, *)
{
animators
.
append
(
MotionTargetStateAnimator
<
MotionViewPropertyViewContext
>
())
}
for
v
in
animators
{
v
.
motion
=
self
}
}
/// Prepares the plugins.
func
preparePlugins
()
{
for
x
in
MotionTransition
.
enabledPlugins
.
map
({
return
$0
.
init
()
})
{
plugins
.
append
(
x
)
}
for
plugin
in
plugins
{
preprocessors
.
append
(
plugin
)
animators
.
append
(
plugin
)
}
}
/// Prepares the transition container.
func
prepareTransitionContainer
()
{
guard
let
v
=
transitionContainer
else
{
return
}
v
.
isUserInteractionEnabled
=
false
// a view to hold all the animating views
container
=
UIView
(
frame
:
v
.
bounds
)
v
.
addSubview
(
container
!
)
transitionContainer
?
.
isUserInteractionEnabled
=
false
}
/// Prepares the view that holds all the animating views.
...
...
@@ -193,24 +179,16 @@ fileprivate extension MotionTransition {
tv
.
updateConstraintsIfNeeded
()
tv
.
setNeedsLayout
()
tv
.
layoutIfNeeded
()
context
.
set
(
fromViews
:
fv
.
flattenedViewHierarchy
,
toViews
:
tv
.
flattenedViewHierarchy
)
}
/// Prepares the view hierarchy.
func
prepareViewHierarchy
()
{
guard
let
fv
=
fromView
else
{
return
}
guard
let
tv
=
toView
else
{
return
}
context
.
set
(
fromViews
:
fv
.
flattenedViewHierarchy
,
toViews
:
tv
.
flattenedViewHierarchy
)
if
(
viewOrderStrategy
==
.
auto
&&
if
(
.
auto
==
viewOrderStrategy
&&
!
isPresenting
&&
!
isTabBarController
)
||
viewOrderStrategy
==
.
sourceViewOnTop
{
.
sourceViewOnTop
==
viewOrderStrategy
{
context
.
insertToViewFirst
=
true
}
}
...
...
@@ -226,6 +204,8 @@ fileprivate extension MotionTransition {
return
false
}
print
(
"FROM PREPARED"
,
animatingFromViews
)
animatingToViews
=
context
.
toViews
.
filter
{
(
view
)
->
Bool
in
for
animator
in
animators
{
if
animator
.
canAnimate
(
view
:
view
,
isAppearing
:
true
)
{
...
...
@@ -234,6 +214,8 @@ fileprivate extension MotionTransition {
}
return
false
}
print
(
"TO PREPARED"
,
animatingFromViews
)
}
/// Prepares the to view.
...
...
Sources/Transition/MotionTransition.swift
View file @
104aead0
...
...
@@ -119,7 +119,7 @@ public protocol MotionViewControllerDelegate {
#### Use the following methods for controlling the interactive transition:
```swift
func update(progress:Double)
func update(progress:
Double)
func end()
func cancel()
func apply(transitions: [MotionTargetState], to view: UIView)
...
...
@@ -348,7 +348,7 @@ open class MotionTransition: NSObject {
internal
var
fullScreenSnapshot
:
UIView
?
/// Progress of the current transition. 0 if no transition is happening.
public
internal(set)
var
elapsedTime
:
TimeInterval
=
0
{
public
internal(set)
var
progress
:
TimeInterval
=
0
{
didSet
{
guard
.
animating
==
state
else
{
return
...
...
@@ -362,7 +362,7 @@ open class MotionTransition: NSObject {
updatePlugins
()
}
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
elapsedTime
))
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
progress
))
}
}
...
...
@@ -418,8 +418,8 @@ open class MotionTransition: NSObject {
}
extension
MotionTransition
:
MotionProgressRunnerDelegate
{
func
update
(
elapsedTime
:
TimeInterval
)
{
self
.
elapsedTime
=
elapsedTime
func
update
(
progress
:
TimeInterval
)
{
self
.
progress
=
progress
}
}
...
...
@@ -446,13 +446,13 @@ fileprivate extension MotionTransition {
}
for
v
in
observers
{
v
.
motion
(
transitionObserver
:
v
,
didUpdateWith
:
elapsedTime
)
v
.
motion
(
transitionObserver
:
v
,
didUpdateWith
:
progress
)
}
}
/// Updates the animators.
func
updateAnimators
()
{
let
t
=
elapsedTime
*
totalDuration
let
t
=
progress
*
totalDuration
for
a
in
animators
{
a
.
seek
(
to
:
t
)
}
...
...
@@ -460,87 +460,13 @@ fileprivate extension MotionTransition {
/// Updates the plugins.
func
updatePlugins
()
{
let
t
=
elapsedTime
*
totalDuration
let
t
=
progress
*
totalDuration
for
p
in
plugins
where
p
.
requirePerFrameCallback
{
p
.
seek
(
to
:
t
)
}
}
}
public
extension
MotionTransition
{
/**
Updates the elapsed time for the interactive transition.
- Parameter elapsedTime t: the current progress, must be between -1...1.
*/
public
func
update
(
_
percentageComplete
:
TimeInterval
)
{
guard
.
animating
==
state
else
{
startingProgress
=
percentageComplete
return
}
progressRunner
.
stop
()
elapsedTime
=
Double
(
CGFloat
(
percentageComplete
)
.
clamp
(
0
,
1
))
}
/**
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.
*/
public
func
cancel
(
isAnimated
:
Bool
=
true
)
{
guard
isTransitioning
else
{
return
}
guard
isAnimated
else
{
complete
(
isFinishing
:
false
)
return
}
var
d
:
TimeInterval
=
0
for
a
in
animators
{
var
t
=
elapsedTime
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.
*/
public
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
)
}
}
}
internal
extension
MotionTransition
{
/**
Checks if a given plugin is enabled.
...
...
@@ -597,30 +523,6 @@ public extension MotionTransition {
}
}
public
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.
*/
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
=
completion
start
()
}
}
internal
extension
MotionTransition
{
/**
Processes the start transition delegation methods.
...
...
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