Commit 732bcdb5 by Daniel Dahan

progress on new Motion structure

parent 3d9641b4
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
965FE9611FDCC3AF0098BDD0 /* MotionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9601FDCC3AF0098BDD0 /* MotionState.swift */; };
965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */; };
965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */; };
96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */; }; 96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093D1F24F7370015A2B5 /* MotionAnimator.swift */; };
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; }; 96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */; };
96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; }; 96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4093F1F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift */; };
...@@ -28,9 +31,7 @@ ...@@ -28,9 +31,7 @@
96E409771F24F7370015A2B5 /* MotionAnimationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */; }; 96E409771F24F7370015A2B5 /* MotionAnimationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */; };
96E409781F24F7370015A2B5 /* MotionCAAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */; }; 96E409781F24F7370015A2B5 /* MotionCAAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */; };
96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409541F24F7370015A2B5 /* MotionContext.swift */; }; 96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409541F24F7370015A2B5 /* MotionContext.swift */; };
96E4097A1F24F7370015A2B5 /* MotionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409551F24F7370015A2B5 /* MotionController.swift */; };
96E4097B1F24F7370015A2B5 /* MotionCoordinateSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */; }; 96E4097B1F24F7370015A2B5 /* MotionCoordinateSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */; };
96E4097C1F24F7370015A2B5 /* MotionIndependentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409571F24F7370015A2B5 /* MotionIndependentController.swift */; };
96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409581F24F7370015A2B5 /* MotionPlugin.swift */; }; 96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409581F24F7370015A2B5 /* MotionPlugin.swift */; };
96E4097E1F24F7370015A2B5 /* MotionSnapshotType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */; }; 96E4097E1F24F7370015A2B5 /* MotionSnapshotType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */; };
96E4097F1F24F7370015A2B5 /* MotionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */; }; 96E4097F1F24F7370015A2B5 /* MotionTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */; };
...@@ -63,9 +64,7 @@ ...@@ -63,9 +64,7 @@
96E4099C1F24F7570015A2B5 /* MotionAnimationState.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E4099C1F24F7570015A2B5 /* MotionAnimationState.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4099D1F24F7570015A2B5 /* MotionCAAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E4099D1F24F7570015A2B5 /* MotionCAAnimation.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4099E1F24F7570015A2B5 /* MotionContext.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409541F24F7370015A2B5 /* MotionContext.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E4099E1F24F7570015A2B5 /* MotionContext.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409541F24F7370015A2B5 /* MotionContext.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E4099F1F24F7570015A2B5 /* MotionController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409551F24F7370015A2B5 /* MotionController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409A01F24F7570015A2B5 /* MotionCoordinateSpace.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E409A01F24F7570015A2B5 /* MotionCoordinateSpace.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409A11F24F7570015A2B5 /* MotionIndependentController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409571F24F7370015A2B5 /* MotionIndependentController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409A21F24F7570015A2B5 /* MotionPlugin.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409581F24F7370015A2B5 /* MotionPlugin.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E409A21F24F7570015A2B5 /* MotionPlugin.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409581F24F7370015A2B5 /* MotionPlugin.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409A31F24F7570015A2B5 /* MotionSnapshotType.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E409A31F24F7570015A2B5 /* MotionSnapshotType.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E409A41F24F7570015A2B5 /* MotionTransition.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E409A41F24F7570015A2B5 /* MotionTransition.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */; settings = {ATTRIBUTES = (Public, ); }; };
...@@ -81,6 +80,9 @@ ...@@ -81,6 +80,9 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
965FE9601FDCC3AF0098BDD0 /* MotionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionState.swift; sourceTree = "<group>"; };
965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Motion+Complete.swift"; sourceTree = "<group>"; };
965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MotionProgressRunner.swift; sourceTree = "<group>"; };
96C98DD11E424AB000B22906 /* Motion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Motion.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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>"; }; 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>"; }; 96E4093E1F24F7370015A2B5 /* MotionAnimatorViewContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimatorViewContext.swift; sourceTree = "<group>"; };
...@@ -103,9 +105,7 @@ ...@@ -103,9 +105,7 @@
96E409521F24F7370015A2B5 /* MotionAnimationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationState.swift; sourceTree = "<group>"; }; 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionAnimationState.swift; sourceTree = "<group>"; };
96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCAAnimation.swift; sourceTree = "<group>"; }; 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCAAnimation.swift; sourceTree = "<group>"; };
96E409541F24F7370015A2B5 /* MotionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionContext.swift; sourceTree = "<group>"; }; 96E409541F24F7370015A2B5 /* MotionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionContext.swift; sourceTree = "<group>"; };
96E409551F24F7370015A2B5 /* MotionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionController.swift; sourceTree = "<group>"; };
96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCoordinateSpace.swift; sourceTree = "<group>"; }; 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionCoordinateSpace.swift; sourceTree = "<group>"; };
96E409571F24F7370015A2B5 /* MotionIndependentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionIndependentController.swift; sourceTree = "<group>"; };
96E409581F24F7370015A2B5 /* MotionPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionPlugin.swift; sourceTree = "<group>"; }; 96E409581F24F7370015A2B5 /* MotionPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionPlugin.swift; sourceTree = "<group>"; };
96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionSnapshotType.swift; sourceTree = "<group>"; }; 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionSnapshotType.swift; sourceTree = "<group>"; };
96E4095A1F24F7370015A2B5 /* MotionTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransition.swift; sourceTree = "<group>"; }; 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MotionTransition.swift; sourceTree = "<group>"; };
...@@ -146,13 +146,14 @@ ...@@ -146,13 +146,14 @@
96E409BC1F24FC300015A2B5 /* LICENSE */, 96E409BC1F24FC300015A2B5 /* LICENSE */,
96E4094F1F24F7370015A2B5 /* Motion.h */, 96E4094F1F24F7370015A2B5 /* Motion.h */,
96E409501F24F7370015A2B5 /* Motion.swift */, 96E409501F24F7370015A2B5 /* Motion.swift */,
965FE9601FDCC3AF0098BDD0 /* MotionState.swift */,
965FE9621FDCCE030098BDD0 /* Motion+Complete.swift */,
965FE9641FDCCE910098BDD0 /* MotionProgressRunner.swift */,
96E409511F24F7370015A2B5 /* MotionAnimation.swift */, 96E409511F24F7370015A2B5 /* MotionAnimation.swift */,
96E409521F24F7370015A2B5 /* MotionAnimationState.swift */, 96E409521F24F7370015A2B5 /* MotionAnimationState.swift */,
96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */, 96E409531F24F7370015A2B5 /* MotionCAAnimation.swift */,
96E409541F24F7370015A2B5 /* MotionContext.swift */, 96E409541F24F7370015A2B5 /* MotionContext.swift */,
96E409551F24F7370015A2B5 /* MotionController.swift */,
96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */, 96E409561F24F7370015A2B5 /* MotionCoordinateSpace.swift */,
96E409571F24F7370015A2B5 /* MotionIndependentController.swift */,
96E409581F24F7370015A2B5 /* MotionPlugin.swift */, 96E409581F24F7370015A2B5 /* MotionPlugin.swift */,
96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */, 96E409591F24F7370015A2B5 /* MotionSnapshotType.swift */,
96E4095A1F24F7370015A2B5 /* MotionTransition.swift */, 96E4095A1F24F7370015A2B5 /* MotionTransition.swift */,
...@@ -235,9 +236,7 @@ ...@@ -235,9 +236,7 @@
96E4099C1F24F7570015A2B5 /* MotionAnimationState.swift in Headers */, 96E4099C1F24F7570015A2B5 /* MotionAnimationState.swift in Headers */,
96E4099D1F24F7570015A2B5 /* MotionCAAnimation.swift in Headers */, 96E4099D1F24F7570015A2B5 /* MotionCAAnimation.swift in Headers */,
96E4099E1F24F7570015A2B5 /* MotionContext.swift in Headers */, 96E4099E1F24F7570015A2B5 /* MotionContext.swift in Headers */,
96E4099F1F24F7570015A2B5 /* MotionController.swift in Headers */,
96E409A01F24F7570015A2B5 /* MotionCoordinateSpace.swift in Headers */, 96E409A01F24F7570015A2B5 /* MotionCoordinateSpace.swift in Headers */,
96E409A11F24F7570015A2B5 /* MotionIndependentController.swift in Headers */,
96E409A21F24F7570015A2B5 /* MotionPlugin.swift in Headers */, 96E409A21F24F7570015A2B5 /* MotionPlugin.swift in Headers */,
96E409A31F24F7570015A2B5 /* MotionSnapshotType.swift in Headers */, 96E409A31F24F7570015A2B5 /* MotionSnapshotType.swift in Headers */,
96E409A41F24F7570015A2B5 /* MotionTransition.swift in Headers */, 96E409A41F24F7570015A2B5 /* MotionTransition.swift in Headers */,
...@@ -314,11 +313,11 @@ ...@@ -314,11 +313,11 @@
96E409731F24F7370015A2B5 /* MotionAnimationFillMode.swift in Sources */, 96E409731F24F7370015A2B5 /* MotionAnimationFillMode.swift in Sources */,
96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */, 96E409791F24F7370015A2B5 /* MotionContext.swift in Sources */,
96E409831F24F7370015A2B5 /* DurationPreprocessor.swift in Sources */, 96E409831F24F7370015A2B5 /* DurationPreprocessor.swift in Sources */,
965FE9631FDCCE030098BDD0 /* Motion+Complete.swift in Sources */,
96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */, 96E4097D1F24F7370015A2B5 /* MotionPlugin.swift in Sources */,
96E409681F24F7370015A2B5 /* MotionHasInsertOrder.swift in Sources */, 96E409681F24F7370015A2B5 /* MotionHasInsertOrder.swift in Sources */,
96E4096E1F24F7370015A2B5 /* Motion+CG.swift in Sources */, 96E4096E1F24F7370015A2B5 /* Motion+CG.swift in Sources */,
96E409851F24F7370015A2B5 /* MatchPreprocessor.swift in Sources */, 96E409851F24F7370015A2B5 /* MatchPreprocessor.swift in Sources */,
96E4097C1F24F7370015A2B5 /* MotionIndependentController.swift in Sources */,
96E409861F24F7370015A2B5 /* MotionPreprocessor.swift in Sources */, 96E409861F24F7370015A2B5 /* MotionPreprocessor.swift in Sources */,
96E409821F24F7370015A2B5 /* CascadePreprocessor.swift in Sources */, 96E409821F24F7370015A2B5 /* CascadePreprocessor.swift in Sources */,
96E4096C1F24F7370015A2B5 /* Motion+CALayer.swift in Sources */, 96E4096C1F24F7370015A2B5 /* Motion+CALayer.swift in Sources */,
...@@ -329,7 +328,6 @@ ...@@ -329,7 +328,6 @@
96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */, 96E409651F24F7370015A2B5 /* MotionAnimator.swift in Sources */,
96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */, 96E409671F24F7370015A2B5 /* MotionCoreAnimationViewContext.swift in Sources */,
96E4096D1F24F7370015A2B5 /* Motion+CAMediaTimingFunction.swift in Sources */, 96E4096D1F24F7370015A2B5 /* Motion+CAMediaTimingFunction.swift in Sources */,
96E4097A1F24F7370015A2B5 /* MotionController.swift in Sources */,
96E409841F24F7370015A2B5 /* IgnoreSubviewModifiersPreprocessor.swift in Sources */, 96E409841F24F7370015A2B5 /* IgnoreSubviewModifiersPreprocessor.swift in Sources */,
96E409771F24F7370015A2B5 /* MotionAnimationState.swift in Sources */, 96E409771F24F7370015A2B5 /* MotionAnimationState.swift in Sources */,
96E409761F24F7370015A2B5 /* MotionAnimation.swift in Sources */, 96E409761F24F7370015A2B5 /* MotionAnimation.swift in Sources */,
...@@ -342,6 +340,8 @@ ...@@ -342,6 +340,8 @@
96E409881F24F7370015A2B5 /* TransitionPreprocessor.swift in Sources */, 96E409881F24F7370015A2B5 /* TransitionPreprocessor.swift in Sources */,
96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */, 96E4096A1F24F7370015A2B5 /* MotionViewPropertyViewContext.swift in Sources */,
96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */, 96E409661F24F7370015A2B5 /* MotionAnimatorViewContext.swift in Sources */,
965FE9651FDCCE910098BDD0 /* MotionProgressRunner.swift in Sources */,
965FE9611FDCC3AF0098BDD0 /* MotionState.swift in Sources */,
96E4097F1F24F7370015A2B5 /* MotionTransition.swift in Sources */, 96E4097F1F24F7370015A2B5 /* MotionTransition.swift in Sources */,
96E409751F24F7370015A2B5 /* Motion.swift in Sources */, 96E409751F24F7370015A2B5 /* Motion.swift in Sources */,
96E4096F1F24F7370015A2B5 /* Motion+Obj-C.swift in Sources */, 96E4096F1F24F7370015A2B5 /* Motion+Obj-C.swift in Sources */,
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "0920"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
......
...@@ -220,6 +220,7 @@ internal class SnapshotWrapperView: UIView { ...@@ -220,6 +220,7 @@ internal class SnapshotWrapperView: UIView {
init(contentView: UIView) { init(contentView: UIView) {
self.contentView = contentView self.contentView = contentView
super.init(frame: contentView.frame) super.init(frame: contentView.frame)
contentView.frame = bounds
addSubview(contentView) addSubview(contentView)
} }
...@@ -229,8 +230,7 @@ internal class SnapshotWrapperView: UIView { ...@@ -229,8 +230,7 @@ internal class SnapshotWrapperView: UIView {
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
contentView.bounds.size = bounds.size contentView.frame = bounds
contentView.center = bounds.center
} }
} }
...@@ -243,13 +243,17 @@ internal extension UIView { ...@@ -243,13 +243,17 @@ internal extension UIView {
if #available(iOS 9.0, *), isHidden && (superview is UICollectionView || superview is UIStackView || self is UITableViewCell) { if #available(iOS 9.0, *), isHidden && (superview is UICollectionView || superview is UIStackView || self is UITableViewCell) {
return [] return []
} else if isHidden && (superview is UICollectionView || self is UITableViewCell) { } else if isHidden && (superview is UICollectionView || self is UITableViewCell) {
return [] return []
} else if isMotionEnabledForSubviews { } else if isMotionEnabledForSubviews {
return [self] + subviews.flatMap { $0.flattenedViewHierarchy } return [self] + subviews.flatMap {
} else { $0.flattenedViewHierarchy
return [self] }
} }
return [self]
} }
/** /**
...@@ -285,7 +289,13 @@ internal extension UIView { ...@@ -285,7 +289,13 @@ internal extension UIView {
*/ */
func slowSnapshotView() -> UIView { func slowSnapshotView() -> UIView {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0) UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
layer.render(in: UIGraphicsGetCurrentContext()!)
guard let currentContext = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return UIView()
}
layer.render(in: currentContext)
let image = UIGraphicsGetImageFromCurrentImageContext() let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() UIGraphicsEndImageContext()
......
/*
* 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 Motion {
/**
Complete the transition.
- Parameter after: A TimeInterval.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
func complete(after: TimeInterval, isFinished: Bool) {
guard isTransitioning else {
return
}
if after <= 0.001 {
complete(isFinished: isFinished)
return
}
let v = (isFinished ? elapsedTime : 1 - elapsedTime) * totalDuration
self.isFinished = isFinished
currentAnimationDuration = after + v
beginTime = CACurrentMediaTime() - v
}
/**
Complete the transition.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinished: Bool) {
if state == .notified {
forceFinishing = isFinished
}
guard .animating == state || .starting == state else {
return
}
defer {
transitionContext = nil
fromViewController = nil
toViewController = nil
isNavigationController = false
isTabBarController = false
forceNonInteractive = false
transitionPairs.removeAll()
transitionObservers = nil
transitionContainer = nil
completionCallback = nil
forceFinishing = nil
container = nil
startingProgress = nil
preprocessors.removeAll()
animators.removeAll()
plugins.removeAll()
context = nil
elapsedTime = 0
totalDuration = 0
state = .possible
}
state = .completing
progressRunner.stop()
context.clean()
if let tv = toView, let fv = fromView {
if isFinished && 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 !isFinished && !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 && isFinished) || (fromOverFullScreen && !isFinished) {
transitionContainer?.addSubview(isFinished ? fv : tv)
}
transitionContainer?.addSubview(isFinished ? tv : fv)
if isPresenting != isFinished, !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?(isFinished)
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
if isFinished {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
} else {
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc)
tContext?.cancelInteractiveTransition()
}
tContext?.completeTransition(isFinished)
}
}
...@@ -125,54 +125,176 @@ public protocol MotionViewControllerDelegate { ...@@ -125,54 +125,176 @@ public protocol MotionViewControllerDelegate {
func apply(transitions: [MotionTransition], to view: UIView) func apply(transitions: [MotionTransition], to view: UIView)
``` ```
*/ */
public class Motion: MotionController { import UIKit
public class Motion: NSObject, MotionProgressRunnerDelegate {
/// Shared singleton object for controlling the transition /// Shared singleton object for controlling the transition
public static let shared = Motion() public static let shared = Motion()
/// Plugins that are enabled during the transition.
internal static var enabledPlugins = [MotionPlugin.Type]()
/// A reference to a fullscreen snapshot.
internal var fullScreenSnapshot: UIView!
/// A reference to the MotionContext.
public internal(set) var context: MotionContext!
/// A boolean indicating whether the transition interactive or not.
public var isInteractive: Bool {
return nil == displayLink
}
/// Source view controller. /// Source view controller.
public internal(set) var fromViewController: UIViewController? public internal(set) var fromViewController: UIViewController?
/// Destination view controller. /// Destination view controller.
public internal(set) var toViewController: UIViewController? public internal(set) var toViewController: UIViewController?
/// Whether or not we are presenting the destination view controller. /// A reference to the fromView, fromViewController.view.
public internal(set) var isPresenting = true internal var fromView: UIView? {
return fromViewController?.view
}
/// A reference to the toView, toViewController.view.
internal var toView: UIView? {
return toViewController?.view
}
/// The color of the transitioning container.
internal var containerBackgroundColor: UIColor?
/**
A UIViewControllerContextTransitioning object provided by UIKit, which
might be nil when isTransitioning. This happens when calling motionReplaceViewController
*/
internal weak var transitionContext: UIViewControllerContextTransitioning?
/// Progress of the current transition, 0 if a transition is not happening. /// Progress of the current transition. 0 if no transition is happening.
public override var elapsedTime: TimeInterval { public internal(set) var elapsedTime: TimeInterval = 0 {
didSet { didSet {
guard isTransitioning else { guard .animating == state else {
return return
} }
updateTransitionObservers()
if isInteractive {
updateAnimators()
} else {
updatePlugins()
}
transitionContext?.updateInteractiveTransition(CGFloat(elapsedTime)) transitionContext?.updateInteractiveTransition(CGFloat(elapsedTime))
} }
} }
/// Indicates whether the transition is animating or not. /// State of the transition.
public var isAnimating = false public internal(set) var state = MotionState.possible {
didSet {
guard .notified != state else {
return
}
guard .starting != state else {
return
}
beginCallback?(.animating == state)
beginCallback = nil
}
}
/// A boolean indicating whether a transition is active.
public var isTransitioning: Bool {
return nil != transitionContainer
}
/** /**
A UIViewControllerContextTransitioning object provided by UIKit, which A view container used to hold all the animating views during a
might be nil when isTransitioning. This happens when calling motionReplaceViewController transition.
*/ */
internal weak var transitionContext: UIViewControllerContextTransitioning? public internal(set) var container: UIView!
/// A reference to a fullscreen snapshot. /// UIKit's supplied transition container.
internal var fullScreenSnapshot: UIView! internal var transitionContainer: UIView?
/// An optional begin callbcak.
internal var beginCallback: ((Bool) -> Void)?
/// An optional completion callback.
internal var completionCallback: ((Bool) -> Void)?
/// Binds the render cycle to the transition animation.
internal var displayLink: CADisplayLink?
/// 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
/// The currently running animation duration.
internal var currentAnimationDuration: TimeInterval = 0
/// A reference to a MotionProgressRunner.
lazy var progressRunner: MotionProgressRunner = {
let runner = MotionProgressRunner()
runner.delegate = self
return runner
}()
/// The start time of the animation.
internal var beginTime: TimeInterval? {
didSet {
guard nil != beginTime else {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
return
}
guard nil == displayLink else {
return
}
displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
}
/// A boolean indicating if the transition has finished.
internal var isFinished = true
/// An Array of MotionPreprocessors used during a transition.
internal lazy var preprocessors = [MotionPreprocessor]()
/// An Array of MotionAnimators used during a transition.
internal lazy var animators = [MotionAnimator]()
/// An Array of MotionPlugins used during a transition.
internal lazy var plugins = [MotionPlugin]()
/// The matching fromViews to toViews based on the motionIdentifier value.
internal lazy var transitionPairs = [(fromViews: [UIView], toViews: [UIView])]()
/// Whether or not we are presenting the destination view controller.
public var isPresenting = true
/// Indicates whether the transition is animating or not.
public var isAnimating = false
/// Default animation type. /// Default animation type.
internal var defaultAnimation = MotionTransitionType.auto internal var defaultAnimation = MotionTransitionType.auto
/// 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. By default, Motion will always appear to be interactive to UIKit. This forces it to appear non-interactive.
Used when doing a motionReplaceViewController within a UINavigationController, to fix a bug with Used when doing a motionReplaceViewController within a UINavigationController, to fix a bug with
UINavigationController.setViewControllers not able to handle interactive transitions. UINavigationController.setViewControllers not able to handle interactive transitions.
*/ */
internal var forceNonInteractive = false internal var forceNonInteractive = false
internal var forceFinishing: Bool?
internal var startingProgress: CGFloat?
/// Inserts the toViews first. /// Inserts the toViews first.
internal var insertToViewFirst = false internal var insertToViewFirst = false
...@@ -206,16 +328,6 @@ public class Motion: MotionController { ...@@ -206,16 +328,6 @@ public class Motion: MotionController {
return !isContainerController && (.overFullScreen == v.modalPresentationStyle || .overCurrentContext == v.modalPresentationStyle) return !isContainerController && (.overFullScreen == v.modalPresentationStyle || .overCurrentContext == v.modalPresentationStyle)
} }
/// A reference to the fromView, fromViewController.view.
internal var fromView: UIView? {
return fromViewController?.view
}
/// A reference to the toView, toViewController.view.
internal var toView: UIView? {
return toViewController?.view
}
/// An initializer. /// An initializer.
internal override init() { internal override init() {
super.init() super.init()
...@@ -223,169 +335,490 @@ public class Motion: MotionController { ...@@ -223,169 +335,490 @@ public class Motion: MotionController {
} }
public extension Motion { public extension Motion {
/// Turn off built-in animations for the next transition.
func disableDefaultAnimationForNextTransition() {
defaultAnimation = .none
}
/** /**
Set the default animation for the next transition. This may override the Receive callbacks on each animation frame.
root-view's motionTransitions during the transition. Observers will be cleaned when a transition completes.
- Parameter animation: A MotionTransitionType. - Parameter observer: A MotionTransitionObserver.
*/ */
func setAnimationForNextTransition(_ animation: MotionTransitionType) { func addTransitionObserver(observer: MotionTransitionObserver) {
defaultAnimation = animation if nil == transitionObservers {
transitionObservers = []
}
transitionObservers?.append(observer)
}
}
private extension Motion {
/// 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.
Set the container background color for the next transition. func updateAnimators() {
- Parameter _ color: An optional UIColor. let t = elapsedTime * totalDuration
*/ for a in animators {
func setContainerBackgroundColorForNextTransition(_ color: UIColor?) { a.seek(to: t)
containerBackgroundColor = color }
}
/// Updates the plugins.
func updatePlugins() {
let t = elapsedTime * totalDuration
for p in plugins where p.requirePerFrameCallback {
p.seek(to: t)
}
} }
} }
public extension Motion { private extension Motion {
/** /**
A helper transition function. Handler for the DisplayLink updates.
- Parameter from: A UIViewController. - Parameter _ link: CADisplayLink.
- 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) { @objc
guard !isTransitioning else { func handleDisplayLink(_ link: CADisplayLink) {
guard isTransitioning else {
return return
} }
isPresenting = true guard 0 < currentAnimationDuration else {
transitionContainer = view return
fromViewController = from }
toViewController = to
completionCallback = completion
start() 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))
}
} }
} }
fileprivate extension Motion { public extension Motion {
/// Starts the transition animation. /**
func start() { Updates the elapsed time for the interactive transition.
- Parameter elapsedTime t: the current progress, must be between -1...1.
*/
public func update(elapsedTime: TimeInterval) {
guard isTransitioning else { guard isTransitioning else {
return return
} }
prepareViewControllers() beginTime = nil
prepareSnapshotView() self.elapsedTime = max(-1, min(1, elapsedTime))
prepareTransition()
prepareContext()
prepareToView()
prepareViewHierarchy()
processContext()
prepareTransitionPairs()
processForAnimation()
} }
}
/**
internal extension Motion { Finish the interactive transition.
override func animate() { Will stop the interactive transition and animate from the
guard let tv = toView else { current state to the **end** state
- Parameter isAnimated: A boolean indicating if the completion is animated.
*/
public func end(isAnimated: Bool = true) {
guard isTransitioning else {
return return
} }
context.unhide(view: tv) guard isAnimated else {
complete(isFinished: true)
return
}
updateContainerBackgroundColor() var t: TimeInterval = 0
updateInsertOrder()
super.animate() for a in animators {
t = max(t, a.resume(at: elapsedTime * totalDuration, isReversed: false))
}
fullScreenSnapshot?.removeFromSuperview() complete(after: t, isFinished: true)
} }
override func complete(isFinished: Bool) { /**
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 { guard isTransitioning else {
return return
} }
guard let c = container else { guard isAnimated else {
complete(isFinished: false)
return return
} }
guard let tc = transitionContainer else { 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, isFinished: 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 transitions: An Array of MotionTransitions.
- Parameter to view: A UIView.
*/
public func apply(transitions: [MotionTransition], to view: UIView) {
guard isTransitioning else {
return return
} }
guard let fv = fromView else { let s = MotionTransitionState(transitions: transitions)
let v = context.transitionPairedView(for: view) ?? view
for a in animators {
a.apply(state: s, to: v)
}
}
}
internal extension Motion {
/**
Load plugins, processors, animators, container, & context
The transitionContainer must already be set.
Subclasses should call context.set(fromViews: toViews) after
inserting fromViews & toViews into the container
*/
@objc
func prepareTransition() {
guard isTransitioning else {
return
}
prepareTransitionContainer()
prepareContext()
preparePreprocessors()
prepareAnimators()
preparePlugins()
}
/// Prepares the transition fromView & toView pairs.
@objc
func prepareTransitionPairs() {
guard isTransitioning else {
return return
} }
for a in animators {
let fv = context.fromViews.filter { (view) -> Bool in
return a.canAnimate(view: view, isAppearing: false)
}
let tv = context.toViews.filter {
return a.canAnimate(view: $0, isAppearing: true)
}
transitionPairs.append((fv, tv))
}
guard let tv = toView else { guard let tv = toView else {
return return
} }
context.clean() context.hide(view: tv)
}
}
internal extension Motion {
/// Executes the preprocessors' process function.
func processContext() {
guard isTransitioning else {
return
}
for x in preprocessors {
x.process(fromViews: context.fromViews, toViews: context.toViews)
}
}
/**
Animates the views. Subclasses should call `prepareTransition` &
`prepareTransitionPairs` before calling `animate`.
*/
@objc
func animate() {
guard .starting == state else {
return
}
state = .animating
if let tv = toView {
context.unhide(view: tv)
}
if isFinished && isPresenting && toOverFullScreen { for (fv, tv) in transitionPairs {
// finished presenting a overFullScreen view controller. for view in fv {
context.unhide(rootView: tv) context.hide(view: view)
context.removeSnapshots(rootView: tv) }
context.storeViewAlpha(rootView: fv)
fromViewController!.motionStoredSnapshot = container for view in tv {
fv.removeFromSuperview() context.hide(view: view)
fv.addSubview(c) }
} else if !isFinished && !isPresenting && fromOverFullScreen { }
// Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv) var t: TimeInterval = 0
context.removeSnapshots(rootView: fv) var b = false
context.storeViewAlpha(rootView: tv)
for (i, a) in animators.enumerated() {
let d = a.animate(fromViews: transitionPairs[i].0, toViews: transitionPairs[i].1)
toViewController!.motionStoredSnapshot = container if .infinity == d {
tv.removeFromSuperview() b = true
tv.addSubview(c) } else {
t = max(t, d)
}
}
totalDuration = t
if b {
update(elapsedTime: 0)
} else { } else {
context.unhideAll() complete(after: t, isFinished: true)
context.removeAllSnapshots()
c.removeFromSuperview()
} }
// Move fromView & toView back from our container back to the one supplied by UIKit. updateContainerBackgroundColor()
if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) { updateInsertOrder()
tc.addSubview(isFinished ? fv : tv)
fullScreenSnapshot?.removeFromSuperview()
}
}
private extension Motion {
/// Prepares the transition container.
func prepareTransitionContainer() {
guard let v = transitionContainer else {
return
} }
tc.addSubview(isFinished ? tv : fv) v.isUserInteractionEnabled = false
// a view to hold all the animating views
container = UIView(frame: v.bounds)
v.addSubview(container!)
}
/// Prepares the preprocessors.
func preparePreprocessors() {
for x in [
IgnoreSubviewTransitionsPreprocessor(),
MatchPreprocessor(),
SourcePreprocessor(),
CascadePreprocessor(),
TransitionPreprocessor(motion: self),
DurationPreprocessor()] as [MotionPreprocessor] {
preprocessors.append(x)
}
if isPresenting != isFinished, !isContainerController { for x in preprocessors {
// Only happens when present a .overFullScreen view controller. x.context = context
// bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication.shared.keyWindow!.addSubview(isPresenting ? fv : tv)
} }
}
/// Prepares the animators.
func prepareAnimators() {
animators.append(MotionTransitionAnimator<MotionCoreAnimationViewContext>())
// use temp variables to remember these values if #available(iOS 10, tvOS 10, *) {
// because we have to reset everything before calling animators.append(MotionTransitionAnimator<MotionViewPropertyViewContext>())
// any delegate or completion block }
let tContext = transitionContext
let fvc = fromViewController
let tvc = toViewController
resetTransition() for v in animators {
v.context = context
}
}
/// Prepares the plugins.
func preparePlugins() {
for x in Motion.enabledPlugins.map({
return $0.init()
}) {
plugins.append(x)
}
super.complete(isFinished: isFinished) for plugin in plugins {
preprocessors.append(plugin)
animators.append(plugin)
}
}
}
private 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
}
if isFinished { for v in animators {
processEndTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc) (v as? MotionHasInsertOrder)?.insertToViewFirst = insertToViewFirst
} else { }
processCancelTransitionDelegation(transitionContext: tContext, fromViewController: fvc, toViewController: tvc) }
}
internal extension Motion {
/**
Checks if a given plugin is enabled.
- Parameter plugin: A MotionPlugin.Type.
- Returns: A boolean indicating if the plugin is enabled or not.
*/
static func isEnabled(plugin: MotionPlugin.Type) -> Bool {
return nil != enabledPlugins.index(where: { return $0 == plugin })
}
/**
Enables a given plugin.
- Parameter plugin: A MotionPlugin.Type.
*/
static func enable(plugin: MotionPlugin.Type) {
disable(plugin: plugin)
enabledPlugins.append(plugin)
}
/**
Disables a given plugin.
- Parameter plugin: A MotionPlugin.Type.
*/
static func disable(plugin: MotionPlugin.Type) {
guard let index = enabledPlugins.index(where: { return $0 == plugin }) else {
return
} }
tContext?.completeTransition(isFinished) enabledPlugins.remove(at: index)
} }
} }
fileprivate extension Motion { internal extension Motion {
// should call this after `prepareTransitionPairs` & before `processContext`
func insert<T>(preprocessor: MotionPreprocessor, before: T.Type) {
let i = preprocessors.index { $0 is T } ?? preprocessors.count
preprocessor.context = context
preprocessors.insert(preprocessor, at: i)
}
}
public extension Motion {
/// Turn off built-in animations for the next transition.
func disableDefaultAnimationForNextTransition() {
defaultAnimation = .none
}
/**
Set the default animation for the next transition. This may override the
root-view's motionTransitions during the transition.
- Parameter animation: A MotionTransitionType.
*/
func setAnimationForNextTransition(_ animation: MotionTransitionType) {
defaultAnimation = animation
}
/**
Set the container background color for the next transition.
- Parameter _ color: An optional UIColor.
*/
func setContainerBackgroundColorForNextTransition(_ color: UIColor?) {
containerBackgroundColor = color
}
}
public 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()
}
}
private extension Motion {
/// Starts the transition animation.
func start() {
guard .notified == state else {
return
}
state = .starting
prepareViewControllers()
prepareSnapshotView()
prepareTransition()
prepareContext()
prepareToView()
prepareViewHierarchy()
processContext()
prepareTransitionPairs()
processForAnimation()
}
}
internal extension Motion {
/// Resets the transition values. /// Resets the transition values.
func resetTransition() { func resetTransition() {
transitionContext = nil transitionContext = nil
...@@ -400,7 +833,7 @@ fileprivate extension Motion { ...@@ -400,7 +833,7 @@ fileprivate extension Motion {
} }
} }
fileprivate extension Motion { internal extension Motion {
/// Prepares the from and to view controllers. /// Prepares the from and to view controllers.
func prepareViewControllers() { func prepareViewControllers() {
processStartTransitionDelegation(fromViewController: fromViewController, toViewController: toViewController) processStartTransitionDelegation(fromViewController: fromViewController, toViewController: toViewController)
...@@ -440,6 +873,8 @@ fileprivate extension Motion { ...@@ -440,6 +873,8 @@ fileprivate extension Motion {
return return
} }
context = MotionContext(container: v)
context.loadViewAlpha(rootView: tv) context.loadViewAlpha(rootView: tv)
v.addSubview(tv) v.addSubview(tv)
...@@ -457,8 +892,12 @@ fileprivate extension Motion { ...@@ -457,8 +892,12 @@ fileprivate extension Motion {
return return
} }
tv.frame = fv.frame if let toViewController = toViewController, let transitionContext = transitionContext {
tv.updateConstraints() tv.frame = transitionContext.finalFrame(for: toViewController)
} else {
tv.frame = fv.frame
}
tv.setNeedsLayout() tv.setNeedsLayout()
tv.layoutIfNeeded() tv.layoutIfNeeded()
} }
...@@ -478,27 +917,11 @@ fileprivate extension Motion { ...@@ -478,27 +917,11 @@ fileprivate extension Motion {
} }
internal extension Motion { internal extension Motion {
override func prepareTransition() {
super.prepareTransition()
insert(preprocessor: TransitionPreprocessor(motion: self), before: DurationPreprocessor.self)
}
override func prepareTransitionPairs() {
super.prepareTransitionPairs()
guard let tv = toView else {
return
}
context.hide(view: tv)
}
}
fileprivate extension Motion {
/// Processes the animations. /// Processes the animations.
func processForAnimation() { func processForAnimation() {
#if os(tvOS) #if os(tvOS)
animate() animate()
#else #else
if isNavigationController { if isNavigationController {
// When animating within navigationController, we have to dispatch later into the main queue. // When animating within navigationController, we have to dispatch later into the main queue.
...@@ -509,6 +932,7 @@ fileprivate extension Motion { ...@@ -509,6 +932,7 @@ fileprivate extension Motion {
} else { } else {
animate() animate()
} }
#endif #endif
} }
...@@ -627,28 +1051,6 @@ fileprivate extension Motion { ...@@ -627,28 +1051,6 @@ fileprivate extension Motion {
} }
} }
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 { internal extension Motion {
/** /**
Helper for processing the MotionViewControllerDelegate. Helper for processing the MotionViewControllerDelegate.
...@@ -696,7 +1098,7 @@ extension Motion: UIViewControllerAnimatedTransitioning { ...@@ -696,7 +1098,7 @@ extension Motion: UIViewControllerAnimatedTransitioning {
- Returns: A TimeInterval that is the total animation time including delays. - Returns: A TimeInterval that is the total animation time including delays.
*/ */
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0 // Will be updated dynamically. return 0 // Time will be updated dynamically.
} }
public func animationEnded(_ transitionCompleted: Bool) { public func animationEnded(_ transitionCompleted: Bool) {
......
...@@ -77,6 +77,7 @@ internal extension MotionContext { ...@@ -77,6 +77,7 @@ internal extension MotionContext {
func set(fromViews: [UIView], toViews: [UIView]) { func set(fromViews: [UIView], toViews: [UIView]) {
self.fromViews = fromViews self.fromViews = fromViews
self.toViews = toViews self.toViews = toViews
map(views: fromViews, identifierMap: &motionIdentifierToSourceView) map(views: fromViews, identifierMap: &motionIdentifierToSourceView)
map(views: toViews, identifierMap: &motionIdentifierToDestinationView) map(views: toViews, identifierMap: &motionIdentifierToDestinationView)
} }
...@@ -114,8 +115,8 @@ public extension MotionContext { ...@@ -114,8 +115,8 @@ public extension MotionContext {
get { get {
return viewToTargetState[view] return viewToTargetState[view]
} }
set { set(value) {
viewToTargetState[view] = newValue viewToTargetState[view] = value
} }
} }
} }
...@@ -185,6 +186,7 @@ public extension MotionContext { ...@@ -185,6 +186,7 @@ public extension MotionContext {
if let visualEffectView = containerView as? UIVisualEffectView { if let visualEffectView = containerView as? UIVisualEffectView {
containerView = visualEffectView.contentView containerView = visualEffectView.contentView
} }
case .global: case .global:
break break
} }
...@@ -221,8 +223,8 @@ public extension MotionContext { ...@@ -221,8 +223,8 @@ public extension MotionContext {
case .optimized: case .optimized:
#if os(tvOS) #if os(tvOS)
snapshot = view.snapshotView(afterScreenUpdates: true)! snapshot = view.snapshotView(afterScreenUpdates: true)!
#else
#else
if #available(iOS 9.0, *), let stackView = view as? UIStackView { if #available(iOS 9.0, *), let stackView = view as? UIStackView {
snapshot = stackView.slowSnapshotView() snapshot = stackView.slowSnapshotView()
...@@ -246,7 +248,6 @@ public extension MotionContext { ...@@ -246,7 +248,6 @@ public extension MotionContext {
// take a snapshot without the background // take a snapshot without the background
barView.layer.sublayers![0].opacity = 0 barView.layer.sublayers![0].opacity = 0
let realSnapshot = barView.snapshotView(afterScreenUpdates: true)! let realSnapshot = barView.snapshotView(afterScreenUpdates: true)!
barView.layer.sublayers![0].opacity = 1 barView.layer.sublayers![0].opacity = 1
...@@ -260,6 +261,7 @@ public extension MotionContext { ...@@ -260,6 +261,7 @@ public extension MotionContext {
} else { } else {
snapshot = view.snapshotView() ?? UIView() snapshot = view.snapshotView() ?? UIView()
} }
#endif #endif
} }
...@@ -267,6 +269,7 @@ public extension MotionContext { ...@@ -267,6 +269,7 @@ public extension MotionContext {
if let imageView = view as? UIImageView, imageView.adjustsImageWhenAncestorFocused { if let imageView = view as? UIImageView, imageView.adjustsImageWhenAncestorFocused {
snapshot.frame = imageView.focusedFrameGuide.layoutFrame snapshot.frame = imageView.focusedFrameGuide.layoutFrame
} }
#endif #endif
view.layer.cornerRadius = oldCornerRadius view.layer.cornerRadius = oldCornerRadius
...@@ -323,6 +326,7 @@ public extension MotionContext { ...@@ -323,6 +326,7 @@ public extension MotionContext {
for sibling in nextSiblings { for sibling in nextSiblings {
insertGlobalViewTree(view: sibling) insertGlobalViewTree(view: sibling)
} }
} else { } else {
containerView.addSubview(snapshot) containerView.addSubview(snapshot)
} }
...@@ -363,7 +367,7 @@ internal extension MotionContext { ...@@ -363,7 +367,7 @@ internal extension MotionContext {
- Parameter view: A UIView. - Parameter view: A UIView.
*/ */
func hide(view: UIView) { func hide(view: UIView) {
guard nil == viewToAlphas[view], .noSnapshot != self[view]?.snapshotType else { guard nil == viewToAlphas[view] else {
return return
} }
...@@ -372,7 +376,7 @@ internal extension MotionContext { ...@@ -372,7 +376,7 @@ internal extension MotionContext {
viewToAlphas[view] = 1 viewToAlphas[view] = 1
} else { } else {
viewToAlphas[view] = view.isOpaque ? .infinity : view.alpha viewToAlphas[view] = view.alpha
view.alpha = 0 view.alpha = 0
} }
} }
...@@ -389,10 +393,6 @@ internal extension MotionContext { ...@@ -389,10 +393,6 @@ internal extension MotionContext {
if view is UIVisualEffectView { if view is UIVisualEffectView {
view.isHidden = false view.isHidden = false
} else if oldAlpha == .infinity {
view.alpha = 1
view.isOpaque = true
} else { } else {
view.alpha = oldAlpha view.alpha = oldAlpha
} }
......
/*
* 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
public class MotionController: NSObject {
/// A reference to the MotionContext.
public internal(set) var context: MotionContext!
/// 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 elapsedTime: TimeInterval = 0 {
didSet {
guard isTransitioning else {
return
}
updateTransitionObservers()
guard isInteractive else {
updatePlugins()
return
}
updateAnimators()
}
}
/// A boolean indicating whether a transition is active.
public var isTransitioning: Bool {
return nil != transitionContainer
}
/**
A view container used to hold all the animating views during a
transition.
*/
public internal(set) var container: UIView?
/// 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?
/// 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
/// The currently running animation duration.
internal var currentAnimationDuration: TimeInterval = 0
/// The start time of the animation.
internal var beginTime: TimeInterval? {
didSet {
guard nil != beginTime else {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
displayLink = nil
return
}
guard nil == displayLink else {
return
}
displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode(rawValue: RunLoopMode.commonModes.rawValue))
}
}
/// A boolean indicating if the transition has finished.
internal var isFinished = true
/// An Array of MotionPreprocessors used during a transition.
internal fileprivate(set) lazy var preprocessors = [MotionPreprocessor]()
/// An Array of MotionAnimators used during a transition.
internal fileprivate(set) lazy var animators = [MotionAnimator]()
/// An Array of MotionPlugins used during a transition.
internal fileprivate(set) lazy var plugins = [MotionPlugin]()
/// The matching fromViews to toViews based on the motionIdentifier value.
internal fileprivate(set) lazy var transitionPairs = [(fromViews: [UIView], toViews: [UIView])]()
/// Plugins that are enabled during the transition.
internal static var enabledPlugins = [MotionPlugin.Type]()
/// Initializer.
internal override init() {}
}
public extension MotionController {
/**
Receive callbacks on each animation frame.
Observers will be cleaned when a transition completes.
- Parameter observer: A MotionTransitionObserver.
*/
func addTransitionObserver(observer: MotionTransitionObserver) {
if nil == transitionObservers {
transitionObservers = []
}
transitionObservers?.append(observer)
}
}
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 t = elapsedTime * totalDuration
for a in animators {
a.seek(to: t)
}
}
/// Updates the plugins.
func updatePlugins() {
let t = elapsedTime * totalDuration
for p in plugins where p.requirePerFrameCallback {
p.seek(to: t)
}
}
}
fileprivate extension MotionController {
/**
Handler for the DisplayLink updates.
- Parameter _ link: CADisplayLink.
*/
@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 {
/**
Updates the elapsed time for the interactive transition.
- Parameter elapsedTime t: the current progress, must be between -1...1.
*/
public func update(elapsedTime t: TimeInterval) {
guard isTransitioning else {
return
}
beginTime = nil
elapsedTime = max(-1, min(1, t))
}
/**
Finish the interactive transition.
Will stop the interactive transition and animate from the
current state to the **end** state
- Parameter isAnimated: A boolean indicating if the completion is animated.
*/
public func end(isAnimated: Bool = true) {
guard isTransitioning else {
return
}
guard isAnimated else {
complete(isFinished: true)
return
}
var t: TimeInterval = 0
for a in animators {
t = max(t, a.resume(at: elapsedTime * totalDuration, isReversed: false))
}
complete(after: t, isFinished: 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.
*/
public func cancel(isAnimated: Bool = true) {
guard isTransitioning else {
return
}
guard isAnimated else {
complete(isFinished: 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, isFinished: 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 transitions: An Array of MotionTransitions.
- Parameter to view: A UIView.
*/
public func apply(transitions: [MotionTransition], to view: UIView) {
guard isTransitioning else {
return
}
let s = MotionTransitionState(transitions: transitions)
let v = context.transitionPairedView(for: view) ?? view
for a in animators {
a.apply(state: s, to: v)
}
}
}
internal extension MotionController {
/**
Load plugins, processors, animators, container, & context
The transitionContainer must already be set.
Subclasses should call context.set(fromViews: toViews) after
inserting fromViews & toViews into the container
*/
@objc
func prepareTransition() {
guard isTransitioning else {
return
}
prepareTransitionContainer()
prepareContext()
preparePreprocessors()
prepareAnimators()
preparePlugins()
}
/// Prepares the transition fromView & toView pairs.
@objc
func prepareTransitionPairs() {
guard isTransitioning else {
return
}
for a in animators {
let fv = context.fromViews.filter { (view: UIView) -> Bool in
return a.canAnimate(view: view, isAppearing: false)
}
let tv = context.toViews.filter {
return a.canAnimate(view: $0, isAppearing: true)
}
transitionPairs.append((fv, tv))
}
}
}
internal extension MotionController {
/// Executes the preprocessors' process function.
func processContext() {
guard isTransitioning else {
return
}
for x in preprocessors {
x.process(fromViews: context.fromViews, toViews: context.toViews)
}
}
/**
Animates the views. Subclasses should call `prepareTransition` &
`prepareTransitionPairs` before calling `animate`.
*/
@objc
func animate() {
guard isTransitioning else {
return
}
for (fv, tv) in transitionPairs {
for view in fv {
context.hide(view: view)
}
for view in tv {
context.hide(view: view)
}
}
var t: TimeInterval = 0
var b = false
for (i, a) in animators.enumerated() {
let d = a.animate(fromViews: transitionPairs[i].0, toViews: transitionPairs[i].1)
if .infinity == d {
b = true
} else {
t = max(t, d)
}
}
totalDuration = t
if b {
update(elapsedTime: 0)
} else {
complete(after: t, isFinished: true)
}
}
/**
Complete the transition.
- Parameter after: A TimeInterval.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
func complete(after: TimeInterval, isFinished: Bool) {
guard isTransitioning else {
return
}
if after <= 0.001 {
complete(isFinished: isFinished)
return
}
let v = (isFinished ? elapsedTime : 1 - elapsedTime) * totalDuration
self.isFinished = isFinished
currentAnimationDuration = after + v
beginTime = CACurrentMediaTime() - v
}
/**
Complete the transition.
- Parameter isFinished: A Boolean indicating if the transition
has completed.
*/
@objc
func complete(isFinished: Bool) {
guard isTransitioning else {
return
}
for a in animators {
a.clean()
}
transitionContainer?.isUserInteractionEnabled = true
let completion = completionCallback
transitionObservers = nil
transitionContainer = nil
completionCallback = nil
container = nil
context = nil
beginTime = nil
elapsedTime = 0
totalDuration = 0
preprocessors.removeAll()
animators.removeAll()
plugins.removeAll()
transitionPairs.removeAll()
completion?(isFinished)
}
}
fileprivate extension MotionController {
/// 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!)
}
/// Prepares the context.
func prepareContext() {
guard let v = container else {
return
}
context = MotionContext(container: v)
}
/// Prepares the preprocessors.
func preparePreprocessors() {
for x in [
IgnoreSubviewTransitionsPreprocessor(),
MatchPreprocessor(),
SourcePreprocessor(),
CascadePreprocessor(),
DurationPreprocessor()] as [MotionPreprocessor] {
preprocessors.append(x)
}
for x in preprocessors {
x.context = context
}
}
/// Prepares the animators.
func prepareAnimators() {
animators.append(MotionTransitionAnimator<MotionCoreAnimationViewContext>())
if #available(iOS 10, tvOS 10, *) {
animators.append(MotionTransitionAnimator<MotionViewPropertyViewContext>())
}
for v in animators {
v.context = context
}
}
/// Prepares the plugins.
func preparePlugins() {
for x in Motion.enabledPlugins.map({
return $0.init()
}) {
plugins.append(x)
}
for plugin in plugins {
preprocessors.append(plugin)
animators.append(plugin)
}
}
}
internal extension MotionController {
/**
Checks if a given plugin is enabled.
- Parameter plugin: A MotionPlugin.Type.
- Returns: A boolean indicating if the plugin is enabled or not.
*/
static func isEnabled(plugin: MotionPlugin.Type) -> Bool {
return nil != enabledPlugins.index(where: { return $0 == plugin })
}
/**
Enables a given plugin.
- Parameter plugin: A MotionPlugin.Type.
*/
static func enable(plugin: MotionPlugin.Type) {
disable(plugin: plugin)
enabledPlugins.append(plugin)
}
/**
Disables a given plugin.
- Parameter plugin: A MotionPlugin.Type.
*/
static func disable(plugin: MotionPlugin.Type) {
guard let index = enabledPlugins.index(where: { return $0 == plugin }) else {
return
}
enabledPlugins.remove(at: index)
}
}
internal extension MotionController {
// should call this after `prepareTransitionPairs` & before `processContext`
func insert<T>(preprocessor: MotionPreprocessor, before: T.Type) {
let i = preprocessors.index { $0 is T } ?? preprocessors.count
preprocessor.context = context
preprocessors.insert(preprocessor, at: i)
}
}
/*
* 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
protocol MotionProgressRunnerDelegate: class {
func update(elapsedTime: TimeInterval)
func complete(isFinished: Bool)
}
class MotionProgressRunner {
weak var delegate: MotionProgressRunnerDelegate?
var isRunning: Bool {
return displayLink != nil
}
internal var timePassed: TimeInterval = 0
internal var duration: TimeInterval = 0
internal var displayLink: CADisplayLink?
internal var isReversed: Bool = false
@objc
func displayUpdate(_ link: CADisplayLink) {
timePassed += isReversed ? -link.duration : link.duration
if isReversed, timePassed <= 1.0 / 120 {
delegate?.complete(isFinished: false)
stop()
return
}
if !isReversed, timePassed > duration - 1.0 / 120 {
delegate?.complete(isFinished: true)
stop()
return
}
delegate?.update(elapsedTime: timePassed / duration)
}
func start(timePassed: TimeInterval, totalTime: TimeInterval, reverse: Bool) {
stop()
self.timePassed = timePassed
self.isReversed = reverse
self.duration = totalTime
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
}
}
...@@ -26,42 +26,23 @@ ...@@ -26,42 +26,23 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import UIKit @objc(MotionState)
public enum MotionState: Int {
public class MotionIndependentController: MotionController { /// Motion is able to start a new transition.
/// An initializer. case possible
public override init() {
super.init() /// UIKit has notified Motion about a pending transition.
} /// Motion hasn't started preparation.
case notified
/**
Transitions source views to their corresponding destination view /// Motion's `start` method has been called. Preparing the animation.
within a given root view. case starting
- Parameter rootView: A UIView.
- Parameter fromViews: An Array of UIViews. /// Motions's `animate` method has been called. Animation has started.
- Parameter toViews: An Array of UIViews. case animating
- Parameter completion: An optional callback.
*/ /// Motions's `complete` method has been called. Transition has ended or has
public func transition(rootView: UIView, fromViews: [UIView], toViews: [UIView], completion: ((Bool) -> Void)? = nil) { /// been cancelled. Motion is cleaning up.
transitionContainer = rootView case completing
completionCallback = completion
prepareTransition()
prepareContext(fromViews: fromViews, toViews: toViews)
prepareTransitionPairs()
animate()
}
} }
fileprivate extension MotionIndependentController {
/**
Prepares the context.
- Parameter fromViews: An Array of UIViews.
- PArameter toViews: An Array of UIViews.
*/
func prepareContext(fromViews: [UIView], toViews: [UIView]) {
context.set(fromViews: fromViews, toViews: toViews)
processContext()
}
}
...@@ -218,7 +218,7 @@ class TransitionPreprocessor: MotionPreprocessor { ...@@ -218,7 +218,7 @@ class TransitionPreprocessor: MotionPreprocessor {
if case .auto = defaultAnimation { if case .auto = defaultAnimation {
if isNavigationController, let navAnim = toViewController?.navigationController?.motionNavigationTransitionType { if isNavigationController, let navAnim = toViewController?.navigationController?.motionNavigationTransitionType {
defaultAnimation = navAnim defaultAnimation = navAnim
} else if isTabBarController, let tabAnim = toViewController?.tabBarController?.motionTabBarTransitionType { } else if isTabBarController, let tabAnim = toViewController?.tabBarController?.motionTabBarTransitionType {
defaultAnimation = tabAnim defaultAnimation = tabAnim
......
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