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
19664a2f
Unverified
Commit
19664a2f
authored
Jun 07, 2017
by
Daniel Dahan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
partial work completed for reworking Motion and MotionController
parent
baf1bf57
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
370 additions
and
277 deletions
+370
-277
Motion.xcodeproj/project.pbxproj
+1
-1
Sources/Animator/MotionCoreAnimationViewContext.swift
+5
-0
Sources/Debug Plugin/MotionDebugPlugin.swift
+4
-4
Sources/DefaultAnimationPreprocessor.swift
+3
-3
Sources/Extensions/Motion+UIViewController.swift
+2
-1
Sources/Motion.swift
+343
-258
Sources/MotionContext.swift
+2
-2
Sources/MotionController.swift
+4
-2
Sources/MotionTargetState.swift
+6
-6
No files found.
Motion.xcodeproj/project.pbxproj
View file @
19664a2f
...
...
@@ -198,8 +198,8 @@
96AEB6691EE4610F009A3BE0
/* Debug Plugin */
,
96AEB66D1EE4610F009A3BE0
/* Extensions */
,
96AEB6771EE4610F009A3BE0
/* Motion.swift */
,
963150D41EE51C7A002B0D42
/* MotionAnimation.swift */
,
96AEB6781EE4610F009A3BE0
/* MotionController.swift */
,
963150D41EE51C7A002B0D42
/* MotionAnimation.swift */
,
96AEB6791EE4610F009A3BE0
/* MotionContext.swift */
,
96AEB67A1EE4610F009A3BE0
/* MotionIndependentController.swift */
,
96AEB67B1EE4610F009A3BE0
/* MotionTransition.swift */
,
...
...
Sources/Animator/MotionCoreAnimationViewContext.swift
View file @
19664a2f
...
...
@@ -410,6 +410,11 @@ extension MotionCoreAnimationViewContext {
return
values
}
/**
Moves a layer's animation to a given elapsed time.
- Parameter layer: A CALayer.
- Parameter elapsedTime: A TimeInterval.
*/
fileprivate
func
seek
(
layer
:
CALayer
,
elapsedTime
:
TimeInterval
)
{
let
timeOffset
=
elapsedTime
-
targetState
.
delay
for
(
key
,
anim
)
in
layer
.
animations
{
...
...
Sources/Debug Plugin/MotionDebugPlugin.swift
View file @
19664a2f
...
...
@@ -38,13 +38,13 @@ public class MotionDebugPlugin: MotionPlugin {
var
updating
=
false
override
public
func
animate
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
->
TimeInterval
{
if
Motion
.
shared
.
forceNo
t
Interactive
{
return
0
}
if
Motion
.
shared
.
forceNo
n
Interactive
{
return
0
}
var
hasArc
=
false
for
v
in
context
.
fromViews
+
context
.
toViews
where
context
[
v
]?
.
arc
!=
nil
&&
context
[
v
]?
.
position
!=
nil
{
hasArc
=
true
break
}
let
debugView
=
MotionDebugView
(
initialProcess
:
Motion
.
shared
.
p
resenting
?
0.0
:
1.0
,
showCurveButton
:
hasArc
,
showOnTop
:
MotionDebugPlugin
.
showOnTop
)
let
debugView
=
MotionDebugView
(
initialProcess
:
Motion
.
shared
.
isP
resenting
?
0.0
:
1.0
,
showCurveButton
:
hasArc
,
showOnTop
:
MotionDebugPlugin
.
showOnTop
)
debugView
.
frame
=
Motion
.
shared
.
container
.
bounds
debugView
.
delegate
=
self
UIApplication
.
shared
.
keyWindow
!.
addSubview
(
debugView
)
...
...
@@ -81,7 +81,7 @@ public class MotionDebugPlugin: MotionPlugin {
extension
MotionDebugPlugin
:
MotionDebugViewDelegate
{
public
func
onDone
()
{
guard
let
debugView
=
debugView
else
{
return
}
let
seekValue
=
Motion
.
shared
.
p
resenting
?
debugView
.
progress
:
1.0
-
debugView
.
progress
let
seekValue
=
Motion
.
shared
.
isP
resenting
?
debugView
.
progress
:
1.0
-
debugView
.
progress
if
seekValue
>
0.5
{
Motion
.
shared
.
end
()
}
else
{
...
...
@@ -90,7 +90,7 @@ extension MotionDebugPlugin:MotionDebugViewDelegate {
}
public
func
onProcessSliderChanged
(
progress
:
Float
)
{
let
seekValue
=
Motion
.
shared
.
p
resenting
?
progress
:
1.0
-
progress
let
seekValue
=
Motion
.
shared
.
isP
resenting
?
progress
:
1.0
-
progress
Motion
.
shared
.
update
(
progress
:
Double
(
seekValue
))
}
...
...
Sources/DefaultAnimationPreprocessor.swift
View file @
19664a2f
...
...
@@ -235,11 +235,11 @@ class DefaultAnimationPreprocessor: BasePreprocessor {
override
func
process
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
{
guard
let
motion
=
motion
else
{
return
}
var
defaultAnimation
=
motion
.
defaultAnimation
let
inNavigationController
=
motion
.
i
n
NavigationController
let
inTabBarController
=
motion
.
i
n
TabBarController
let
inNavigationController
=
motion
.
i
s
NavigationController
let
inTabBarController
=
motion
.
i
s
TabBarController
let
toViewController
=
motion
.
toViewController
let
fromViewController
=
motion
.
fromViewController
let
presenting
=
motion
.
p
resenting
let
presenting
=
motion
.
isP
resenting
let
fromOverFullScreen
=
motion
.
fromOverFullScreen
let
toOverFullScreen
=
motion
.
toOverFullScreen
let
toView
=
motion
.
toView
...
...
Sources/Extensions/Motion+UIViewController.swift
View file @
19664a2f
...
...
@@ -312,6 +312,7 @@ extension UIViewController {
/**
Replace the current view controller with another view controller on the
navigation/modal stack.
- Parameter with next: A UIViewController.
*/
public
func
motion_replaceViewController
(
with
next
:
UIViewController
)
{
guard
!
Motion
.
shared
.
isTransitioning
else
{
...
...
@@ -328,7 +329,7 @@ extension UIViewController {
}
if
navigationController
.
isMotionEnabled
{
Motion
.
shared
.
forceNo
t
Interactive
=
true
Motion
.
shared
.
forceNo
n
Interactive
=
true
}
navigationController
.
setViewControllers
(
v
,
animated
:
true
)
...
...
Sources/Motion.swift
View file @
19664a2f
...
...
@@ -41,296 +41,381 @@ import UIKit
func update(progress:Double)
func end()
func cancel()
func apply(
modifiers:[MotionTransition], to view:
UIView)
func apply(
transitions: [MotionTransition], to view:
UIView)
```
*/
public
class
Motion
:
MotionController
{
// MARK: Shared Access
/// Shared singleton object for controlling the transition
public
static
let
shared
=
Motion
()
// MARK: Properties
/// destination view controller
public
internal(set)
var
toViewController
:
UIViewController
?
/// source view controller
public
internal(set)
var
fromViewController
:
UIViewController
?
/// whether or not we are presenting the destination view controller
public
internal(set)
var
presenting
=
true
/// progress of the current transition. 0 if no transition is happening
public
override
var
progress
:
Double
{
didSet
{
if
isTransitioning
{
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
progress
))
}
}
}
public
var
isAnimating
:
Bool
=
false
/// a UIViewControllerContextTransitioning object provided by UIKit,
/// might be nil when isTransitioning. This happens when calling motionReplaceViewController
internal
weak
var
transitionContext
:
UIViewControllerContextTransitioning
?
internal
var
fullScreenSnapshot
:
UIView
!
internal
var
defaultAnimation
:
MotionDefaultAnimationType
=
.
auto
internal
var
containerColor
:
UIColor
?
// By default, Motion will always appear to be interactive to UIKit. This forces it to appear non-interactive.
// Used when doing a motion_replaceViewController within a UINavigationController, to fix a bug with
// UINavigationController.setViewControllers not able to handle interactive transition
internal
var
forceNotInteractive
=
false
internal
var
insertToViewFirst
=
false
internal
var
inNavigationController
=
false
internal
var
inTabBarController
=
false
internal
var
inContainerController
:
Bool
{
return
inNavigationController
||
inTabBarController
}
internal
var
toOverFullScreen
:
Bool
{
return
!
inContainerController
&&
(
toViewController
!.
modalPresentationStyle
==
.
overFullScreen
||
toViewController
!.
modalPresentationStyle
==
.
overCurrentContext
)
}
internal
var
fromOverFullScreen
:
Bool
{
return
!
inContainerController
&&
(
fromViewController
!.
modalPresentationStyle
==
.
overFullScreen
||
fromViewController
!.
modalPresentationStyle
==
.
overCurrentContext
)
}
/// Shared singleton object for controlling the transition
public
static
let
shared
=
Motion
()
internal
var
toView
:
UIView
{
return
toViewController
!.
view
}
internal
var
fromView
:
UIView
{
return
fromViewController
!.
view
}
/// Source view controller.
public
internal(set)
var
fromViewController
:
UIViewController
?
internal
override
init
()
{
super
.
init
()
}
}
/// Destination view controller.
public
internal(set)
var
toViewController
:
UIViewController
?
/// Whether or not we are presenting the destination view controller.
public
internal(set)
var
isPresenting
=
true
/// Progress of the current transition, 0 if a transition is not happening.
public
override
var
progress
:
Double
{
didSet
{
if
isTransitioning
{
transitionContext
?
.
updateInteractiveTransition
(
CGFloat
(
progress
))
}
}
}
public
extension
Motion
{
/// Indicates whether the transition is animating or not.
public
var
isAnimating
=
false
/**
A UIViewControllerContextTransitioning object provided by UIKit, which
might be nil when isTransitioning. This happens when calling motion_replaceViewController
*/
internal
weak
var
transitionContext
:
UIViewControllerContextTransitioning
?
/// Turn off built-in animation for next transition
func
disableDefaultAnimationForNextTransition
()
{
defaultAnimation
=
.
none
}
/// A reference to a fullscreen snapshot.
internal
var
fullScreenSnapshot
:
UIView
!
/// Set the default animation for next transition
/// This usually overrides rootView's motionTransitions during the transition
///
/// - Parameter animation: animation type
func
setDefaultAnimationForNextTransition
(
_
animation
:
MotionDefaultAnimationType
)
{
defaultAnimation
=
animation
}
/// Default animation type.
internal
var
defaultAnimation
=
MotionDefaultAnimationType
.
auto
/// Set the container color for next transition
///
/// - Parameter color: container color
func
setContainerColorForNextTransition
(
_
color
:
UIColor
?)
{
containerColor
=
color
}
}
/// The color of the transitioning container.
internal
var
containerBackgroundColor
:
UIColor
?
/**
By default, Motion will always appear to be interactive to UIKit. This forces it to appear non-interactive.
Used when doing a motion_replaceViewController within a UINavigationController, to fix a bug with
UINavigationController.setViewControllers not able to handle interactive transitions.
*/
internal
var
forceNonInteractive
=
false
// internal methods for transition
internal
extension
Motion
{
func
start
()
{
guard
isTransitioning
else
{
return
}
if
let
fvc
=
fromViewController
,
let
tvc
=
toViewController
{
closureProcessForMotionDelegate
(
vc
:
fvc
)
{
$0
.
motionWillStartTransition
?()
$0
.
motionWillStartAnimatingTo
?(
viewController
:
tvc
)
}
closureProcessForMotionDelegate
(
vc
:
tvc
)
{
$0
.
motionWillStartTransition
?()
$0
.
motionWillStartAnimatingFrom
?(
viewController
:
fvc
)
}
/// Inserts the to-views first.
internal
var
insertToViewFirst
=
false
/// Indicates whether a UINavigationController is transitioning.
internal
var
isNavigationController
=
false
/// Indicates whether a UITabBarController is transitioning.
internal
var
isTabBarController
=
false
/// Indicates whether a UINavigationController or UITabBarController is transitioning.
internal
var
isContainerController
:
Bool
{
return
isNavigationController
||
isTabBarController
}
// take a snapshot to hide all the flashing that might happen
fullScreenSnapshot
=
transitionContainer
.
window
?
.
snapshotView
(
afterScreenUpdates
:
true
)
??
fromView
.
snapshotView
(
afterScreenUpdates
:
true
)
(
transitionContainer
.
window
??
transitionContainer
)?
.
addSubview
(
fullScreenSnapshot
)
if
let
oldSnapshot
=
fromViewController
?
.
motionStoredSnapshot
{
oldSnapshot
.
removeFromSuperview
()
fromViewController
?
.
motionStoredSnapshot
=
nil
/// Indicates whether the from view controller is full screen.
internal
var
fromOverFullScreen
:
Bool
{
return
!
isContainerController
&&
(
.
overFullScreen
==
fromViewController
!.
modalPresentationStyle
||
.
overCurrentContext
==
fromViewController
!.
modalPresentationStyle
)
}
if
let
oldSnapshot
=
toViewController
?
.
motionStoredSnapshot
{
oldSnapshot
.
removeFromSuperview
()
toViewController
?
.
motionStoredSnapshot
=
nil
/// Indicates whether the to view controller is full screen.
internal
var
toOverFullScreen
:
Bool
{
return
!
isContainerController
&&
(
.
overFullScreen
==
toViewController
!.
modalPresentationStyle
||
.
overCurrentContext
==
toViewController
!.
modalPresentationStyle
)
}
prepareForTransition
()
insert
(
preprocessor
:
DefaultAnimationPreprocessor
(
motion
:
self
),
before
:
DurationPreprocessor
.
self
)
context
.
loadViewAlpha
(
rootView
:
toView
)
context
.
loadViewAlpha
(
rootView
:
fromView
)
container
.
addSubview
(
toView
)
container
.
addSubview
(
fromView
)
toView
.
frame
=
fromView
.
frame
toView
.
updateConstraints
()
toView
.
setNeedsLayout
()
toView
.
layoutIfNeeded
()
context
.
set
(
fromViews
:
fromView
.
flattenedViewHierarchy
,
toViews
:
toView
.
flattenedViewHierarchy
)
processContext
()
prepareForAnimation
()
context
.
hide
(
view
:
toView
)
#if os(tvOS)
animate
()
#else
if
inNavigationController
{
// 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
{
self
.
animate
()
}
}
else
{
animate
()
}
#endif
}
override
func
animate
()
{
context
.
unhide
(
view
:
toView
)
/// A reference to the from-view, fromViewController.view.
internal
var
fromView
:
UIView
{
return
fromViewController
!.
view
}
/// A reference to the to-view, toViewController.view.
internal
var
toView
:
UIView
{
return
toViewController
!.
view
}
if
let
containerColor
=
containerColor
{
container
.
backgroundColor
=
containerColor
}
else
if
!
toOverFullScreen
&&
!
fromOverFullScreen
{
container
.
backgroundColor
=
toView
.
backgroundColor
/// An initializer.
internal
override
init
()
{
super
.
init
()
}
}
if
fromOverFullScreen
{
insertToViewFirst
=
true
public
extension
Motion
{
/// Turn off built-in animations for the next transition.
func
disableDefaultAnimationForNextTransition
()
{
defaultAnimation
=
.
none
}
for
animator
in
animators
{
if
let
animator
=
animator
as?
MotionHasInsertOrder
{
animator
.
insertToViewFirst
=
insertToViewFirst
}
/**
Set the default animation for the next transition. This may override the
root-view's motionTransitions during the transition.
- Parameter animation: A MotionDefaultAnimationType.
*/
func
setDefaultAnimationForNextTransition
(
_
animation
:
MotionDefaultAnimationType
)
{
defaultAnimation
=
animation
}
super
.
animate
()
fullScreenSnapshot
!.
removeFromSuperview
()
}
override
func
complete
(
finished
:
Bool
)
{
guard
isTransitioning
else
{
return
}
context
.
clean
()
if
finished
&&
presenting
&&
toOverFullScreen
{
// finished presenting a overFullScreen VC
context
.
unhide
(
rootView
:
toView
)
context
.
removeSnapshots
(
rootView
:
toView
)
context
.
storeViewAlpha
(
rootView
:
fromView
)
fromViewController
!.
motionStoredSnapshot
=
container
fromView
.
removeFromSuperview
()
fromView
.
addSubview
(
container
)
}
else
if
!
finished
&&
!
presenting
&&
fromOverFullScreen
{
// cancelled dismissing a overFullScreen VC
context
.
unhide
(
rootView
:
fromView
)
context
.
removeSnapshots
(
rootView
:
fromView
)
context
.
storeViewAlpha
(
rootView
:
toView
)
toViewController
!.
motionStoredSnapshot
=
container
toView
.
removeFromSuperview
()
toView
.
addSubview
(
container
)
}
else
{
context
.
unhideAll
()
context
.
removeAllSnapshots
()
container
.
removeFromSuperview
()
/**
Set the container background color for the next transition.
- Parameter _ color: An optional UIColor.
*/
func
setContainerBackgroundColorForNextTransition
(
_
color
:
UIColor
?)
{
containerBackgroundColor
=
color
}
}
// move fromView & toView back from our container back to the one supplied by UIKit
if
(
toOverFullScreen
&&
finished
)
||
(
fromOverFullScreen
&&
!
finished
)
{
transitionContainer
.
addSubview
(
finished
?
fromView
:
toView
)
internal
extension
Motion
{
/// Starts the transition animation.
func
start
()
{
guard
isTransitioning
else
{
return
}
prepareViewControllers
()
prepareSnapshotView
()
prepareForTransition
()
prepareMotionContext
()
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
}
transitionContainer
.
addSubview
(
finished
?
toView
:
fromView
)
if
presenting
!=
finished
,
!
inContainerController
{
// only happens when present a .overFullScreen VC
// bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication
.
shared
.
keyWindow
!.
addSubview
(
presenting
?
fromView
:
toView
)
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
}
}
super
.
animate
()
fullScreenSnapshot
!.
removeFromSuperview
()
}
// use temp variables to remember these values
// because we have to reset everything before calling
// any delegate or completion block
let
tContext
=
transitionContext
let
fvc
=
fromViewController
let
tvc
=
toViewController
transitionContext
=
nil
fromViewController
=
nil
toViewController
=
nil
containerColor
=
nil
inNavigationController
=
false
inTabBarController
=
false
forceNotInteractive
=
false
insertToViewFirst
=
false
defaultAnimation
=
.
auto
super
.
complete
(
finished
:
finished
)
if
finished
{
if
let
fvc
=
fvc
,
let
tvc
=
tvc
{
closureProcessForMotionDelegate
(
vc
:
fvc
)
{
$0
.
motionDidEndAnimatingTo
?(
viewController
:
tvc
)
$0
.
motionDidEndTransition
?()
override
func
complete
(
finished
:
Bool
)
{
guard
isTransitioning
else
{
return
}
closureProcessForMotionDelegate
(
vc
:
tvc
)
{
$0
.
motionDidEndAnimatingFrom
?(
viewController
:
fvc
)
$0
.
motionDidEndTransition
?()
context
.
clean
()
if
finished
&&
isPresenting
&&
toOverFullScreen
{
// finished presenting a overFullScreen VC
context
.
unhide
(
rootView
:
toView
)
context
.
removeSnapshots
(
rootView
:
toView
)
context
.
storeViewAlpha
(
rootView
:
fromView
)
fromViewController
!.
motionStoredSnapshot
=
container
fromView
.
removeFromSuperview
()
fromView
.
addSubview
(
container
)
}
else
if
!
finished
&&
!
isPresenting
&&
fromOverFullScreen
{
// cancelled dismissing a overFullScreen VC
context
.
unhide
(
rootView
:
fromView
)
context
.
removeSnapshots
(
rootView
:
fromView
)
context
.
storeViewAlpha
(
rootView
:
toView
)
toViewController
!.
motionStoredSnapshot
=
container
toView
.
removeFromSuperview
()
toView
.
addSubview
(
container
)
}
else
{
context
.
unhideAll
()
context
.
removeAllSnapshots
()
container
.
removeFromSuperview
()
}
// move fromView & toView back from our container back to the one supplied by UIKit
if
(
toOverFullScreen
&&
finished
)
||
(
fromOverFullScreen
&&
!
finished
)
{
transitionContainer
.
addSubview
(
finished
?
fromView
:
toView
)
}
}
tContext
?
.
finishInteractiveTransition
(
)
}
else
{
if
let
fvc
=
fvc
,
let
tvc
=
tvc
{
closureProcessForMotionDelegate
(
vc
:
fvc
)
{
$0
.
motionDidCancelAnimatingTo
?(
viewController
:
tvc
)
$0
.
motionDidCancelTransition
?(
)
transitionContainer
.
addSubview
(
finished
?
toView
:
fromView
)
if
isPresenting
!=
finished
,
!
isContainerController
{
// only happens when present a .overFullScreen VC
// bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication
.
shared
.
keyWindow
!.
addSubview
(
isPresenting
?
fromView
:
toView
)
}
closureProcessForMotionDelegate
(
vc
:
tvc
)
{
$0
.
motionDidCancelAnimatingFrom
?(
viewController
:
fvc
)
$0
.
motionDidCancelTransition
?()
// use temp variables to remember these values
// because we have to reset everything before calling
// any delegate or completion block
let
tContext
=
transitionContext
let
fvc
=
fromViewController
let
tvc
=
toViewController
transitionContext
=
nil
fromViewController
=
nil
toViewController
=
nil
containerBackgroundColor
=
nil
isNavigationController
=
false
isTabBarController
=
false
forceNonInteractive
=
false
insertToViewFirst
=
false
defaultAnimation
=
.
auto
super
.
complete
(
finished
:
finished
)
if
finished
{
if
let
fvc
=
fvc
,
let
tvc
=
tvc
{
processForMotionDelegate
(
viewController
:
fvc
)
{
$0
.
motionDidEndAnimatingTo
?(
viewController
:
tvc
)
$0
.
motionDidEndTransition
?()
}
processForMotionDelegate
(
viewController
:
tvc
)
{
$0
.
motionDidEndAnimatingFrom
?(
viewController
:
fvc
)
$0
.
motionDidEndTransition
?()
}
}
tContext
?
.
finishInteractiveTransition
()
}
else
{
if
let
fvc
=
fvc
,
let
tvc
=
tvc
{
processForMotionDelegate
(
viewController
:
fvc
)
{
$0
.
motionDidCancelAnimatingTo
?(
viewController
:
tvc
)
$0
.
motionDidCancelTransition
?()
}
processForMotionDelegate
(
viewController
:
tvc
)
{
$0
.
motionDidCancelAnimatingFrom
?(
viewController
:
fvc
)
$0
.
motionDidCancelTransition
?()
}
}
tContext
?
.
cancelInteractiveTransition
()
}
}
tContext
?
.
cancelInteractiveTransition
(
)
tContext
?
.
completeTransition
(
finished
)
}
tContext
?
.
completeTransition
(
finished
)
}
}
// custom transition helper, used in motion_replaceViewController
internal
extension
Motion
{
func
transition
(
from
:
UIViewController
,
to
:
UIViewController
,
in
view
:
UIView
,
completion
:
((
Bool
)
->
Void
)?
=
nil
)
{
guard
!
isTransitioning
else
{
return
}
presenting
=
true
transitionContainer
=
view
fromViewController
=
from
toViewController
=
to
completionCallback
=
completion
start
()
}
fileprivate
extension
Motion
{
/// Prepares the from and to view controllers.
func
prepareViewControllers
()
{
guard
let
fvc
=
fromViewController
,
let
tvc
=
toViewController
else
{
return
}
processForMotionDelegate
(
viewController
:
fvc
)
{
$0
.
motionWillStartTransition
?()
$0
.
motionWillStartAnimatingTo
?(
viewController
:
tvc
)
}
processForMotionDelegate
(
viewController
:
tvc
)
{
$0
.
motionWillStartTransition
?()
$0
.
motionWillStartAnimatingFrom
?(
viewController
:
fvc
)
}
}
/// Prepares the snapshot view, which hides any flashing that may occur.
func
prepareSnapshotView
()
{
fullScreenSnapshot
=
transitionContainer
.
window
?
.
snapshotView
(
afterScreenUpdates
:
true
)
??
fromView
.
snapshotView
(
afterScreenUpdates
:
true
)
(
transitionContainer
.
window
??
transitionContainer
)?
.
addSubview
(
fullScreenSnapshot
)
if
let
v
=
fromViewController
?
.
motionStoredSnapshot
{
v
.
removeFromSuperview
()
fromViewController
?
.
motionStoredSnapshot
=
nil
}
if
let
v
=
toViewController
?
.
motionStoredSnapshot
{
v
.
removeFromSuperview
()
toViewController
?
.
motionStoredSnapshot
=
nil
}
}
/// Prepares the MotionContext instance.
func
prepareMotionContext
()
{
context
.
loadViewAlpha
(
rootView
:
toView
)
context
.
loadViewAlpha
(
rootView
:
fromView
)
container
.
addSubview
(
toView
)
container
.
addSubview
(
fromView
)
}
/// Prepares the toView instance.
func
prepareToView
()
{
toView
.
frame
=
fromView
.
frame
toView
.
updateConstraints
()
toView
.
setNeedsLayout
()
toView
.
layoutIfNeeded
()
}
/// Prepares the view hierarchy.
func
prepareViewHierarchy
()
{
context
.
set
(
fromViews
:
fromView
.
flattenedViewHierarchy
,
toViews
:
toView
.
flattenedViewHierarchy
)
}
}
// delegate helper
internal
extension
Motion
{
func
closureProcessForMotionDelegate
<
T
:
UIViewController
>
(
vc
:
T
,
closure
:
(
MotionViewControllerDelegate
)
->
Void
)
{
if
let
delegate
=
vc
as?
MotionViewControllerDelegate
{
closure
(
delegate
)
override
func
prepareForTransition
(
)
{
super
.
prepareForTransition
()
insert
(
preprocessor
:
DefaultAnimationPreprocessor
(
motion
:
self
),
before
:
DurationPreprocessor
.
self
)
}
}
if
let
navigationController
=
vc
as?
UINavigationController
,
let
delegate
=
navigationController
.
topViewController
as?
MotionViewControllerDelegate
{
closure
(
delegate
)
internal
extension
Motion
{
/**
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
}
isPresenting
=
true
transitionContainer
=
view
fromViewController
=
from
toViewController
=
to
completionCallback
=
completion
start
()
}
}
if
let
tabBarController
=
vc
as?
UITabBarController
,
let
delegate
=
tabBarController
.
viewControllers
?[
tabBarController
.
selectedIndex
]
as?
MotionViewControllerDelegate
{
closure
(
delegate
)
internal
extension
Motion
{
/**
Helper for processing the MotionViewControllerDelegate.
- Parameter viewController: A UIViewController of type `T`.
- Parameter execute: A callback for execution during processing.
*/
func
processForMotionDelegate
<
T
:
UIViewController
>
(
viewController
:
T
,
execute
:
(
MotionViewControllerDelegate
)
->
Void
)
{
if
let
delegate
=
viewController
as?
MotionViewControllerDelegate
{
execute
(
delegate
)
}
if
let
v
=
viewController
as?
UINavigationController
,
let
delegate
=
v
.
topViewController
as?
MotionViewControllerDelegate
{
execute
(
delegate
)
}
if
let
v
=
viewController
as?
UITabBarController
,
let
delegate
=
v
.
viewControllers
?[
v
.
selectedIndex
]
as?
MotionViewControllerDelegate
{
execute
(
delegate
)
}
}
}
}
// MARK: UIKit Protocol Conformance
...
...
@@ -359,18 +444,18 @@ extension Motion: UIViewControllerAnimatedTransitioning {
extension
Motion
:
UIViewControllerTransitioningDelegate
{
var
interactiveTransitioning
:
UIViewControllerInteractiveTransitioning
?
{
return
forceNo
t
Interactive
?
nil
:
self
return
forceNo
n
Interactive
?
nil
:
self
}
public
func
animationController
(
forPresented
presented
:
UIViewController
,
presenting
:
UIViewController
,
source
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
self
.
p
resenting
=
true
self
.
isP
resenting
=
true
self
.
fromViewController
=
fromViewController
??
presenting
self
.
toViewController
=
toViewController
??
presented
return
self
}
public
func
animationController
(
forDismissed
dismissed
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
self
.
p
resenting
=
false
self
.
isP
resenting
=
false
self
.
fromViewController
=
fromViewController
??
dismissed
return
self
}
...
...
@@ -395,10 +480,10 @@ extension Motion: UIViewControllerInteractiveTransitioning {
extension
Motion
:
UINavigationControllerDelegate
{
public
func
navigationController
(
_
navigationController
:
UINavigationController
,
animationControllerFor
operation
:
UINavigationControllerOperation
,
from
fromVC
:
UIViewController
,
to
toVC
:
UIViewController
)
->
UIViewControllerAnimatedTransitioning
?
{
self
.
p
resenting
=
operation
==
.
push
self
.
isP
resenting
=
operation
==
.
push
self
.
fromViewController
=
fromViewController
??
fromVC
self
.
toViewController
=
toViewController
??
toVC
self
.
i
n
NavigationController
=
true
self
.
i
s
NavigationController
=
true
return
self
}
...
...
@@ -420,10 +505,10 @@ extension Motion: UITabBarControllerDelegate {
isAnimating
=
true
let
fromVCIndex
=
tabBarController
.
childViewControllers
.
index
(
of
:
fromVC
)
!
let
toVCIndex
=
tabBarController
.
childViewControllers
.
index
(
of
:
toVC
)
!
self
.
p
resenting
=
toVCIndex
>
fromVCIndex
self
.
isP
resenting
=
toVCIndex
>
fromVCIndex
self
.
fromViewController
=
fromViewController
??
fromVC
self
.
toViewController
=
toViewController
??
toVC
self
.
i
n
TabBarController
=
true
self
.
i
s
TabBarController
=
true
return
self
}
}
...
...
Sources/MotionContext.swift
View file @
19664a2f
...
...
@@ -56,8 +56,8 @@ public class MotionContext {
if
let
motionIdentifier
=
view
.
motionIdentifier
{
idMap
[
motionIdentifier
]
=
view
}
if
let
modifier
s
=
view
.
motionTransitions
{
targetStates
[
view
]
=
MotionTargetState
(
modifiers
:
modifier
s
)
if
let
transition
s
=
view
.
motionTransitions
{
targetStates
[
view
]
=
MotionTargetState
(
transitions
:
transition
s
)
}
}
}
...
...
Sources/MotionController.swift
View file @
19664a2f
...
...
@@ -195,9 +195,9 @@ public extension MotionController {
- modifiers: the modifiers to override
- view: the view to override to
*/
public
func
apply
(
modifier
s
:
[
MotionTransition
],
to
view
:
UIView
)
{
public
func
apply
(
transition
s
:
[
MotionTransition
],
to
view
:
UIView
)
{
guard
isTransitioning
else
{
return
}
let
targetState
=
MotionTargetState
(
modifiers
:
modifier
s
)
let
targetState
=
MotionTargetState
(
transitions
:
transition
s
)
if
let
otherView
=
self
.
context
.
pairedView
(
for
:
view
)
{
for
animator
in
self
.
animators
{
animator
.
apply
(
state
:
targetState
,
to
:
otherView
)
...
...
@@ -272,6 +272,8 @@ internal extension MotionController {
}
}
func
processContext
()
{
guard
isTransitioning
else
{
fatalError
()
}
for
processor
in
processors
{
...
...
Sources/MotionTargetState.swift
View file @
19664a2f
...
...
@@ -105,16 +105,16 @@ public struct MotionTargetState {
public
var
forceAnimate
:
Bool
=
false
public
var
custom
:
[
String
:
Any
]?
init
(
modifier
s
:
[
MotionTransition
])
{
append
(
contentsOf
:
modifier
s
)
init
(
transition
s
:
[
MotionTransition
])
{
append
(
contentsOf
:
transition
s
)
}
public
mutating
func
append
(
_
modifier
:
MotionTransition
)
{
modifier
.
apply
(
&
self
)
public
mutating
func
append
(
_
transition
:
MotionTransition
)
{
transition
.
apply
(
&
self
)
}
public
mutating
func
append
(
contentsOf
modifier
s
:
[
MotionTransition
])
{
for
modifier
in
modifier
s
{
public
mutating
func
append
(
contentsOf
transition
s
:
[
MotionTransition
])
{
for
modifier
in
transition
s
{
modifier
.
apply
(
&
self
)
}
}
...
...
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