@apexnative/navigation 1.0.0-alpha.6 → 1.0.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +15 -0
- package/android/CLAUDE.md +19 -10
- package/android/CORE.md +3 -2
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +329 -55
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +26 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +46 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +86 -39
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/doctor/checks/app.js +16 -1
- package/cli/project.js +4 -0
- package/cli/setup/index.js +5 -0
- package/cli/setup/steps/metro.js +46 -0
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +43 -4
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/guides/cli.md +3 -1
- package/docs-md/help/troubleshooting.md +1 -1
- package/docs-md/index.json +29 -1
- package/docs-md/screen-ui/back-and-gestures.md +3 -1
- package/docs-md/screen-ui/bar-buttons.md +1 -1
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
- package/docs-md/start/installation.md +16 -1
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +214 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +49 -3
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/Elements/ANNElementFinder.h +2 -2
- package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
- package/ios/Animations/Elements/ANNElementTransition.h +3 -3
- package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
- package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
- package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
- package/ios/CLAUDE.md +19 -8
- package/ios/CORE.md +1 -1
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNDeepLinkQueue.h +6 -5
- package/ios/Core/ANNDeepLinkQueue.mm +22 -10
- package/ios/Core/ANNSceneDelegate.mm +2 -2
- package/ios/Core/ANNUtils.h +7 -0
- package/ios/Core/ANNUtils.mm +10 -0
- package/ios/Core/ApexNavigation.mm +6 -2
- package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +1 -7
- package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
- package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +153 -11
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +2 -4
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNOptions.h +7 -0
- package/ios/Options/ANNOptions.mm +45 -0
- package/ios/Options/ANNOptionsReader.mm +2 -4
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/React/ANNSurfaceHostingView.h +8 -0
- package/ios/React/ANNSurfaceHostingView.mm +28 -1
- package/ios/React/Slots/ANNSlotHost.mm +1 -3
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TurboModules/ANNTurboModule.mm +46 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +9 -3
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +188 -66
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.js.map +1 -1
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +193 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +7 -2
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +5 -4
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- package/lib/module/engine/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +60 -0
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +26 -0
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +5 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.d.ts.map +1 -1
- package/lib/typescript/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/engine/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- package/lib/typescript/engine/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +7 -0
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/options/animations.d.ts +40 -15
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
- package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
- package/lib/typescript/interfaces/options/modals.d.ts +6 -7
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +8 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +3 -1
- package/src/ARCHITECTURE.md +9 -3
- package/src/adapters/NativeANNTurboModule.ts +7 -0
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +198 -66
- package/src/api/events/navigationEvents.ts +9 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +185 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +21 -0
- package/src/engine/layout.ts +11 -2
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +6 -8
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +70 -0
- package/src/engine/screens.tsx +24 -0
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/options/animations.ts +40 -15
- package/src/interfaces/options/bottomTabs.ts +2 -2
- package/src/interfaces/options/buttons.ts +2 -2
- package/src/interfaces/options/modals.ts +6 -7
- package/src/interfaces/options/stack.ts +8 -3
- package/src/interfaces/options/topBar.ts +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
|
@@ -1,27 +1,47 @@
|
|
|
1
|
+
#import <QuartzCore/QuartzCore.h>
|
|
1
2
|
#import <UIKit/UIKit.h>
|
|
2
3
|
|
|
3
4
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
5
|
|
|
5
|
-
/// What a frame driver moves: a view's properties (`ANNViewTracks`), an element, a shared element's copy.
|
|
6
|
-
///
|
|
6
|
+
/// What a frame driver moves: a view's properties (`ANNViewTracks`), an element, a shared element's copy. It gives its
|
|
7
|
+
/// values for the whole transition at once, as Core Animation's animations on its layers (`ANNKeyframes`); past its end
|
|
8
|
+
/// they hold its end values.
|
|
7
9
|
@protocol ANNFrameClient <NSObject>
|
|
8
10
|
|
|
9
|
-
/// The
|
|
10
|
-
|
|
11
|
+
/// The client's animations for a transition of `length` seconds go onto its layers, all inside the driver's
|
|
12
|
+
/// transaction, so they start together.
|
|
13
|
+
- (void)addAnimationsWithLength:(NSTimeInterval)length;
|
|
11
14
|
|
|
12
15
|
/// The time from which the client holds its end values.
|
|
13
16
|
@property(nonatomic, readonly) NSTimeInterval endTime;
|
|
14
17
|
|
|
15
|
-
///
|
|
16
|
-
/// views of its own removes them. Once; later calls do nothing.
|
|
18
|
+
/// The client's animations go, every value it wrote goes back as it was read before the transition (also at a
|
|
19
|
+
/// cancel), and a client that made views of its own removes them. Once; later calls do nothing.
|
|
17
20
|
- (void)restore;
|
|
18
21
|
|
|
19
22
|
@end
|
|
20
23
|
|
|
21
|
-
/// The
|
|
22
|
-
/// a
|
|
23
|
-
///
|
|
24
|
-
///
|
|
24
|
+
/// The times a client samples its values at, in seconds from the start: 0, every 1/120 s up to `end` (wider apart for
|
|
25
|
+
/// a transition over 20 s, 2400 samples at most), each of `marks` within it (a track's start and end, a swap's), `end`
|
|
26
|
+
/// and `length`; ascending, without repeats. The values between two samples are linear, so a kink of a track falls on
|
|
27
|
+
/// a sample.
|
|
28
|
+
FOUNDATION_EXPORT NSArray<NSNumber *> *ANNSampleTimes(NSTimeInterval end, NSTimeInterval length,
|
|
29
|
+
NSArray<NSNumber *> *marks);
|
|
30
|
+
|
|
31
|
+
/// The animation of `keyPath` through `values` at `times` (from `ANNSampleTimes`) over `length` seconds: linear
|
|
32
|
+
/// between them, holding the first value before and the last after (until it is removed). Nil for no length, and for
|
|
33
|
+
/// values and times that don't pair up.
|
|
34
|
+
FOUNDATION_EXPORT CAKeyframeAnimation *_Nullable ANNKeyframes(NSString *keyPath, NSArray *values,
|
|
35
|
+
NSArray<NSNumber *> *times,
|
|
36
|
+
NSTimeInterval length);
|
|
37
|
+
|
|
38
|
+
/// The key of the core's animation of `keyPath` on a layer, for its `restore`.
|
|
39
|
+
FOUNDATION_EXPORT NSString *ANNAnimationKey(NSString *keyPath);
|
|
40
|
+
|
|
41
|
+
/// The frames of one running transition, in Core Animation: every client adds its animations in one transaction, which
|
|
42
|
+
/// starts them together at its commit; the render server plays them, so a busy main queue no longer stops the
|
|
43
|
+
/// transition. Once every animation ended (or was removed) and `length` passed, `done` runs once on the main queue.
|
|
44
|
+
/// Main queue.
|
|
25
45
|
@interface ANNFrameDriver : NSObject
|
|
26
46
|
|
|
27
47
|
- (instancetype)initWithClients:(NSArray<id<ANNFrameClient>> *)clients
|
|
@@ -30,17 +50,37 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
30
50
|
- (instancetype)init NS_UNAVAILABLE;
|
|
31
51
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
32
52
|
|
|
33
|
-
/// The clients
|
|
34
|
-
///
|
|
53
|
+
/// The clients add their animations, which start at the commit of the current transaction (the first frame shows the
|
|
54
|
+
/// start values). `done` comes later, never inside this call.
|
|
35
55
|
- (void)startWithDone:(dispatch_block_t)done;
|
|
36
56
|
|
|
37
|
-
/// The
|
|
38
|
-
///
|
|
57
|
+
/// The start of a transition that follows a gesture: the clients add their animations as `startWithDone:` does, under
|
|
58
|
+
/// the time of `clock` (the transition's container), which stops. `setProgress:` shows them at a part of the length;
|
|
59
|
+
/// `finishWithSpeed:` plays the rest and `rewindWithSpeed:` plays them back to the start, both in Core Animation. The
|
|
60
|
+
/// other animations under `clock` stop with it. Before `done`, which comes once after either of them (never inside a
|
|
61
|
+
/// call) with whether the transition finished, `clock` gets its time back; the caller restores the clients inside
|
|
62
|
+
/// `done` (one commit with it), or they would show at the clock's time for a frame.
|
|
63
|
+
- (void)startPausedOn:(CALayer *)clock done:(void (^)(BOOL finished))done;
|
|
64
|
+
|
|
65
|
+
/// The animations show at `progress` (0 to 1) of the length. Nothing once the end plays.
|
|
66
|
+
- (void)setProgress:(CGFloat)progress;
|
|
67
|
+
|
|
68
|
+
/// The rest of the animations plays from where they show, `speed` times as fast as they would (1 or more).
|
|
69
|
+
- (void)finishWithSpeed:(CGFloat)speed;
|
|
70
|
+
|
|
71
|
+
/// The animations play back to their start, `speed` times as fast as they would (1 or more).
|
|
72
|
+
- (void)rewindWithSpeed:(CGFloat)speed;
|
|
73
|
+
|
|
74
|
+
/// The transition stops (a destroy, a reload, a newer `setRoot`): `done` never runs, and a stopped `clock` gets its
|
|
75
|
+
/// time back. The caller restores the clients, which removes their animations, in the same commit.
|
|
39
76
|
- (void)cancel;
|
|
40
77
|
|
|
41
|
-
/// The
|
|
78
|
+
/// The animations run.
|
|
42
79
|
@property(nonatomic, readonly) BOOL isRunning;
|
|
43
80
|
|
|
81
|
+
/// The seconds from the start to the end of the last client, the transition's own length at least.
|
|
82
|
+
@property(nonatomic, readonly) NSTimeInterval length;
|
|
83
|
+
|
|
44
84
|
@end
|
|
45
85
|
|
|
46
86
|
NS_ASSUME_NONNULL_END
|
|
@@ -2,39 +2,84 @@
|
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
3
|
#import "ANNCurve.h"
|
|
4
4
|
#import "ANNTransformParts.h"
|
|
5
|
-
#
|
|
5
|
+
#include <cmath>
|
|
6
6
|
|
|
7
|
-
// The
|
|
8
|
-
|
|
9
|
-
@interface ANNFrameTarget : NSObject
|
|
10
|
-
@property(nonatomic, weak) id owner;
|
|
11
|
-
@end
|
|
7
|
+
// The samples per second of a client's values: a frame of a 120 Hz display each.
|
|
8
|
+
static const double ANNSamplesPerSecond = 120;
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@end
|
|
10
|
+
// At most this many samples per client: a longer transition samples less often.
|
|
11
|
+
static const NSUInteger ANNMaxSamples = 2400;
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
NSArray<NSNumber *> *ANNSampleTimes(NSTimeInterval end, NSTimeInterval length,
|
|
14
|
+
NSArray<NSNumber *> *marks) {
|
|
15
|
+
end = MIN(MAX(end, 0), length);
|
|
16
|
+
NSMutableArray<NSNumber *> *candidates = [NSMutableArray arrayWithObject:@0];
|
|
17
|
+
double step = MAX(1 / ANNSamplesPerSecond, end / ANNMaxSamples);
|
|
18
|
+
NSUInteger count = (NSUInteger)std::floor(end / step);
|
|
19
|
+
for (NSUInteger index = 1; index <= count; index++) {
|
|
20
|
+
[candidates addObject:@(index * step)];
|
|
21
|
+
}
|
|
22
|
+
for (NSNumber *mark in marks) {
|
|
23
|
+
double time = mark.doubleValue;
|
|
24
|
+
if (std::isfinite(time) && time > 0 && time < end) {
|
|
25
|
+
[candidates addObject:mark];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
[candidates addObject:@(end)];
|
|
29
|
+
[candidates addObject:@(length)];
|
|
30
|
+
[candidates sortUsingSelector:@selector(compare:)];
|
|
31
|
+
NSMutableArray<NSNumber *> *times = [NSMutableArray arrayWithCapacity:candidates.count];
|
|
32
|
+
for (NSNumber *time in candidates) {
|
|
33
|
+
// Samples closer than a microsecond are one.
|
|
34
|
+
if (times.count == 0 || time.doubleValue - times.lastObject.doubleValue > 1e-6) {
|
|
35
|
+
[times addObject:time];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return times;
|
|
39
|
+
}
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
CAKeyframeAnimation *ANNKeyframes(NSString *keyPath, NSArray *values, NSArray<NSNumber *> *times,
|
|
42
|
+
NSTimeInterval length) {
|
|
43
|
+
if (length <= 0 || values.count == 0 || values.count != times.count) {
|
|
44
|
+
return nil;
|
|
45
|
+
}
|
|
46
|
+
NSMutableArray<NSNumber *> *keyTimes = [NSMutableArray arrayWithCapacity:times.count];
|
|
47
|
+
for (NSNumber *time in times) {
|
|
48
|
+
[keyTimes addObject:@(MIN(MAX(time.doubleValue / length, 0.0), 1.0))];
|
|
24
49
|
}
|
|
25
|
-
[
|
|
50
|
+
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keyPath];
|
|
51
|
+
animation.values = values;
|
|
52
|
+
animation.keyTimes = keyTimes;
|
|
53
|
+
animation.calculationMode = kCAAnimationLinear;
|
|
54
|
+
animation.duration = length;
|
|
55
|
+
animation.fillMode = kCAFillModeBoth;
|
|
56
|
+
animation.removedOnCompletion = NO;
|
|
57
|
+
return animation;
|
|
26
58
|
}
|
|
27
59
|
|
|
28
|
-
|
|
60
|
+
NSString *ANNAnimationKey(NSString *keyPath) {
|
|
61
|
+
return [@"ApexNavigation." stringByAppendingString:keyPath];
|
|
62
|
+
}
|
|
29
63
|
|
|
30
64
|
@implementation ANNFrameDriver {
|
|
31
65
|
NSArray<id<ANNFrameClient>> *_clients;
|
|
32
66
|
NSTimeInterval _length;
|
|
33
|
-
CADisplayLink *_link;
|
|
34
|
-
CFTimeInterval _start;
|
|
35
67
|
dispatch_block_t _done;
|
|
36
|
-
|
|
37
|
-
|
|
68
|
+
BOOL _running;
|
|
69
|
+
BOOL _starting;
|
|
70
|
+
// When the animations were added; they start at the commit of the current transaction, a moment later.
|
|
71
|
+
CFTimeInterval _start;
|
|
72
|
+
// The driver keeps itself while its animations run: the transition that started it needn't.
|
|
73
|
+
ANNFrameDriver *_keep;
|
|
74
|
+
// A transition that follows a gesture: the layer whose time stops, its timing before, the clock's time the
|
|
75
|
+
// animations started at, the `done` that tells how it ended, and whether its end plays.
|
|
76
|
+
CALayer *_clock;
|
|
77
|
+
float _clockSpeed;
|
|
78
|
+
CFTimeInterval _clockBegin;
|
|
79
|
+
CFTimeInterval _clockOffset;
|
|
80
|
+
CFTimeInterval _base;
|
|
81
|
+
void (^_pausedDone)(BOOL finished);
|
|
82
|
+
BOOL _ending;
|
|
38
83
|
}
|
|
39
84
|
|
|
40
85
|
- (instancetype)initWithClients:(NSArray<id<ANNFrameClient>> *)clients
|
|
@@ -51,7 +96,11 @@
|
|
|
51
96
|
}
|
|
52
97
|
|
|
53
98
|
- (BOOL)isRunning {
|
|
54
|
-
return
|
|
99
|
+
return _running;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
- (NSTimeInterval)length {
|
|
103
|
+
return _length;
|
|
55
104
|
}
|
|
56
105
|
|
|
57
106
|
- (void)startWithDone:(dispatch_block_t)done {
|
|
@@ -60,69 +109,174 @@
|
|
|
60
109
|
ANNCurveSelfCheck();
|
|
61
110
|
ANNTransformPartsSelfCheck();
|
|
62
111
|
#endif
|
|
63
|
-
if (
|
|
112
|
+
if (_running || _done || _pausedDone) {
|
|
64
113
|
return;
|
|
65
114
|
}
|
|
66
115
|
_done = [done copy];
|
|
67
|
-
_running =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
target.owner = self;
|
|
71
|
-
_link = [CADisplayLink displayLinkWithTarget:target selector:@selector(frame:)];
|
|
72
|
-
[_link addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];
|
|
116
|
+
_running = YES;
|
|
117
|
+
_keep = self;
|
|
118
|
+
_starting = YES;
|
|
73
119
|
_start = CACurrentMediaTime();
|
|
120
|
+
__weak ANNFrameDriver *weakSelf = self;
|
|
121
|
+
[CATransaction begin];
|
|
122
|
+
[CATransaction setDisableActions:YES];
|
|
123
|
+
// Called once every animation added in this transaction ended or was removed (with its layer too), and at once
|
|
124
|
+
// without any.
|
|
125
|
+
[CATransaction setCompletionBlock:^{
|
|
126
|
+
[weakSelf animationsEnded];
|
|
127
|
+
}];
|
|
128
|
+
@try {
|
|
129
|
+
for (id<ANNFrameClient> client in _clients) {
|
|
130
|
+
[client addAnimationsWithLength:_length];
|
|
131
|
+
}
|
|
132
|
+
} @finally {
|
|
133
|
+
[CATransaction commit];
|
|
134
|
+
_starting = NO;
|
|
135
|
+
}
|
|
74
136
|
}
|
|
75
137
|
|
|
76
|
-
- (void)
|
|
77
|
-
if (
|
|
78
|
-
[link invalidate];
|
|
138
|
+
- (void)animationsEnded {
|
|
139
|
+
if (!_running) {
|
|
79
140
|
return;
|
|
80
141
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
_running = nil;
|
|
91
|
-
if (done) {
|
|
92
|
-
done();
|
|
93
|
-
}
|
|
142
|
+
// `done` never comes inside `startWithDone:`, nor before the length passed: a transition with nothing to play (no
|
|
143
|
+
// client, every element skipped) or whose animations went early still takes its time, as on Android.
|
|
144
|
+
__weak ANNFrameDriver *weakSelf = self;
|
|
145
|
+
NSTimeInterval left = _length - (CACurrentMediaTime() - _start);
|
|
146
|
+
if (_starting || left > 0) {
|
|
147
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MAX(left, 0) * NSEC_PER_SEC)),
|
|
148
|
+
dispatch_get_main_queue(), ^{
|
|
149
|
+
[weakSelf animationsEnded];
|
|
150
|
+
});
|
|
94
151
|
return;
|
|
95
152
|
}
|
|
96
|
-
|
|
153
|
+
_running = NO;
|
|
154
|
+
dispatch_block_t done = _done;
|
|
155
|
+
_done = nil;
|
|
156
|
+
_keep = nil;
|
|
157
|
+
if (done) {
|
|
158
|
+
done();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
- (void)cancel {
|
|
163
|
+
ANNAssertMainQueue();
|
|
164
|
+
if (!_running) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
_running = NO;
|
|
168
|
+
_done = nil;
|
|
169
|
+
_pausedDone = nil;
|
|
170
|
+
_keep = nil;
|
|
171
|
+
[self releaseClock];
|
|
97
172
|
}
|
|
98
173
|
|
|
99
|
-
-
|
|
174
|
+
#pragma mark - Following a gesture
|
|
175
|
+
|
|
176
|
+
- (void)startPausedOn:(CALayer *)clock done:(void (^)(BOOL finished))done {
|
|
177
|
+
ANNAssertMainQueue();
|
|
178
|
+
if (_running || _done || _pausedDone) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
_pausedDone = [done copy];
|
|
182
|
+
_running = YES;
|
|
183
|
+
_keep = self;
|
|
184
|
+
_clock = clock;
|
|
185
|
+
_clockSpeed = clock.speed;
|
|
186
|
+
_clockBegin = clock.beginTime;
|
|
187
|
+
_clockOffset = clock.timeOffset;
|
|
188
|
+
// The clock stops at its time now: what else animates under it holds still, and the animations added now start
|
|
189
|
+
// there (Core Animation gives an animation without a begin time the layer's time at the commit).
|
|
190
|
+
_base = [clock convertTime:CACurrentMediaTime() fromLayer:nil];
|
|
100
191
|
[CATransaction begin];
|
|
101
192
|
[CATransaction setDisableActions:YES];
|
|
102
|
-
|
|
103
|
-
|
|
193
|
+
clock.speed = 0;
|
|
194
|
+
clock.beginTime = 0;
|
|
195
|
+
clock.timeOffset = _base;
|
|
196
|
+
@try {
|
|
197
|
+
for (id<ANNFrameClient> client in _clients) {
|
|
198
|
+
[client addAnimationsWithLength:_length];
|
|
199
|
+
}
|
|
200
|
+
} @finally {
|
|
201
|
+
[CATransaction commit];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
- (void)setProgress:(CGFloat)progress {
|
|
206
|
+
ANNAssertMainQueue();
|
|
207
|
+
if (!_running || !_pausedDone || _ending) {
|
|
208
|
+
return;
|
|
104
209
|
}
|
|
210
|
+
[CATransaction begin];
|
|
211
|
+
[CATransaction setDisableActions:YES];
|
|
212
|
+
_clock.timeOffset = _base + MIN(MAX(progress, 0.0), 1.0) * _length;
|
|
105
213
|
[CATransaction commit];
|
|
106
214
|
}
|
|
107
215
|
|
|
108
|
-
- (void)
|
|
109
|
-
[
|
|
110
|
-
_link = nil;
|
|
216
|
+
- (void)finishWithSpeed:(CGFloat)speed {
|
|
217
|
+
[self playOnForward:YES speed:speed];
|
|
111
218
|
}
|
|
112
219
|
|
|
113
|
-
- (void)
|
|
220
|
+
- (void)rewindWithSpeed:(CGFloat)speed {
|
|
221
|
+
[self playOnForward:NO speed:speed];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// The clock runs again from where it shows, forward or back, and `done` comes once it reached the end or the start.
|
|
225
|
+
- (void)playOnForward:(BOOL)forward speed:(CGFloat)speed {
|
|
114
226
|
ANNAssertMainQueue();
|
|
115
|
-
if (!
|
|
227
|
+
if (!_running || !_pausedDone || _ending) {
|
|
116
228
|
return;
|
|
117
229
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
230
|
+
_ending = YES;
|
|
231
|
+
speed = MAX(speed, 1.0);
|
|
232
|
+
CALayer *clock = _clock;
|
|
233
|
+
CFTimeInterval at = clock.timeOffset - _base;
|
|
234
|
+
CFTimeInterval left = forward ? _length - at : at;
|
|
235
|
+
CALayer *parent = clock.superlayer;
|
|
236
|
+
CFTimeInterval now = parent ? [parent convertTime:CACurrentMediaTime() fromLayer:nil]
|
|
237
|
+
: CACurrentMediaTime();
|
|
238
|
+
[CATransaction begin];
|
|
239
|
+
[CATransaction setDisableActions:YES];
|
|
240
|
+
// The clock's time is (parent's time - beginTime) * speed + timeOffset: it goes on from where it shows.
|
|
241
|
+
clock.beginTime = now;
|
|
242
|
+
clock.speed = (float)(forward ? speed : -speed);
|
|
243
|
+
[CATransaction commit];
|
|
244
|
+
__weak ANNFrameDriver *weakSelf = self;
|
|
245
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MAX(left / speed, 0) * NSEC_PER_SEC)),
|
|
246
|
+
dispatch_get_main_queue(), ^{
|
|
247
|
+
[weakSelf pausedEndedForward:forward];
|
|
248
|
+
});
|
|
122
249
|
}
|
|
123
250
|
|
|
124
|
-
- (void)
|
|
125
|
-
|
|
251
|
+
- (void)pausedEndedForward:(BOOL)forward {
|
|
252
|
+
if (!_running || !_pausedDone) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
_running = NO;
|
|
256
|
+
void (^done)(BOOL) = _pausedDone;
|
|
257
|
+
_pausedDone = nil;
|
|
258
|
+
_keep = nil;
|
|
259
|
+
// The clock's time back and the caller's restore in one commit: between two, the animations would show at the
|
|
260
|
+
// clock's own time for a frame (past their end, or part way).
|
|
261
|
+
[CATransaction begin];
|
|
262
|
+
[self releaseClock];
|
|
263
|
+
done(forward);
|
|
264
|
+
[CATransaction commit];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// The clock gets back the timing it had before the gesture.
|
|
268
|
+
- (void)releaseClock {
|
|
269
|
+
CALayer *clock = _clock;
|
|
270
|
+
_clock = nil;
|
|
271
|
+
if (!clock) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
[CATransaction begin];
|
|
275
|
+
[CATransaction setDisableActions:YES];
|
|
276
|
+
clock.speed = _clockSpeed;
|
|
277
|
+
clock.beginTime = _clockBegin;
|
|
278
|
+
clock.timeOffset = _clockOffset;
|
|
279
|
+
[CATransaction commit];
|
|
126
280
|
}
|
|
127
281
|
|
|
128
282
|
@end
|
|
@@ -12,10 +12,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
12
12
|
/// above the leaving one (push, `setStackRoot`, show) or below it (pop, dismissal), gets its final frame and lays out
|
|
13
13
|
/// (React gets its final size), and hides (alpha 0) until the start; a forward transition starts after its wait
|
|
14
14
|
/// (`ANNTransitionWait`), a backward one at once. Then one frame driver moves the sides that play, the elements in
|
|
15
|
-
/// place and the shared elements' copies; a side that doesn't play doesn't move. At the end, before
|
|
16
|
-
/// `completeTransition:`, the
|
|
17
|
-
///
|
|
18
|
-
/// comes inside `animateTransition:`. Main queue.
|
|
15
|
+
/// place and the shared elements' copies, in Core Animation; a side that doesn't play doesn't move. At the end, before
|
|
16
|
+
/// `completeTransition:`, the animations go, the copies go and the React views show again, and both views get back the
|
|
17
|
+
/// transform and the alpha they had. Only a pop of the swipe back follows a gesture (`animateFollowingGesture:`) and
|
|
18
|
+
/// can end where it began; `completeTransition:` never comes inside `animateTransition:`. Main queue.
|
|
19
19
|
@interface ANNTransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
|
|
20
20
|
|
|
21
21
|
- (instancetype)initWithPlan:(ANNTransitionPlan *)plan NS_DESIGNATED_INITIALIZER;
|
|
@@ -25,8 +25,29 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
25
25
|
|
|
26
26
|
@property(nonatomic, strong, readonly) ANNTransitionPlan *plan;
|
|
27
27
|
|
|
28
|
+
/// The start of an interactive transition (a pop of the swipe back, which never waits): as `animateTransition:`, with
|
|
29
|
+
/// the frames held at their start until `setProgress:` moves them (`ANNFrameDriver startPausedOn:`); then
|
|
30
|
+
/// `finishWithSpeed:` or `rewindWithSpeed:`, after the context's own finish or cancel, plays the end.
|
|
31
|
+
- (void)animateFollowingGesture:(id<UIViewControllerContextTransitioning>)transitionContext;
|
|
32
|
+
|
|
33
|
+
/// The frames show at `progress` (0 to 1) of the length.
|
|
34
|
+
- (void)setProgress:(CGFloat)progress;
|
|
35
|
+
|
|
36
|
+
/// The rest plays (`speed` times as fast, 1 or more), and the transition completes.
|
|
37
|
+
- (void)finishWithSpeed:(CGFloat)speed;
|
|
38
|
+
|
|
39
|
+
/// The frames play back to the start (`speed` times as fast, 1 or more), and the transition completes cancelled.
|
|
40
|
+
- (void)rewindWithSpeed:(CGFloat)speed;
|
|
41
|
+
|
|
42
|
+
/// The seconds the frames take: the plan's length, or the end of a longer element.
|
|
43
|
+
@property(nonatomic, readonly) NSTimeInterval length;
|
|
44
|
+
|
|
45
|
+
/// The transition completed (or was never started).
|
|
46
|
+
@property(nonatomic, readonly) BOOL isCompleted;
|
|
47
|
+
|
|
28
48
|
/// A destroy or a reload during the transition: the wait or the frames stop at the end state, every value written goes
|
|
29
|
-
/// back, and the transition completes so that UIKit isn't left half way
|
|
49
|
+
/// back, and the transition completes so that UIKit isn't left half way (an interactive one finishes first). Nothing
|
|
50
|
+
/// once it completed.
|
|
30
51
|
- (void)cancel;
|
|
31
52
|
|
|
32
53
|
@end
|
|
@@ -33,6 +33,9 @@ static void ANNUpdateRootLayoutOf(UIViewController *node) {
|
|
|
33
33
|
NSArray<id<ANNFrameClient>> *_clients;
|
|
34
34
|
BOOL _began;
|
|
35
35
|
BOOL _completed;
|
|
36
|
+
// The transition follows a gesture (`animateFollowingGesture:`), and its end was chosen.
|
|
37
|
+
BOOL _followsGesture;
|
|
38
|
+
BOOL _endChosen;
|
|
36
39
|
// The container's views of the transition that got a depth for it, and the depth each had.
|
|
37
40
|
NSArray<UIView *> *_depthViews;
|
|
38
41
|
NSArray<NSNumber *> *_depthsBefore;
|
|
@@ -118,6 +121,33 @@ static void ANNUpdateRootLayoutOf(UIViewController *node) {
|
|
|
118
121
|
[self begin];
|
|
119
122
|
}
|
|
120
123
|
|
|
124
|
+
- (void)animateFollowingGesture:(id<UIViewControllerContextTransitioning>)transitionContext {
|
|
125
|
+
_followsGesture = YES;
|
|
126
|
+
[self animateTransition:transitionContext];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
- (void)setProgress:(CGFloat)progress {
|
|
130
|
+
[_driver setProgress:progress];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
- (void)finishWithSpeed:(CGFloat)speed {
|
|
134
|
+
_endChosen = YES;
|
|
135
|
+
[_driver finishWithSpeed:speed];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (void)rewindWithSpeed:(CGFloat)speed {
|
|
139
|
+
_endChosen = YES;
|
|
140
|
+
[_driver rewindWithSpeed:speed];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
- (NSTimeInterval)length {
|
|
144
|
+
return _driver ? _driver.length : _plan.length;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
- (BOOL)isCompleted {
|
|
148
|
+
return _completed || !_context;
|
|
149
|
+
}
|
|
150
|
+
|
|
121
151
|
- (void)animationEnded:(BOOL)transitionCompleted {
|
|
122
152
|
_wait = nil;
|
|
123
153
|
_driver = nil;
|
|
@@ -187,8 +217,9 @@ static void ANNUpdateRootLayoutOf(UIViewController *node) {
|
|
|
187
217
|
duration:_plan.length
|
|
188
218
|
alphaBefore:_leavingAlpha];
|
|
189
219
|
}
|
|
190
|
-
// The arriving view
|
|
191
|
-
|
|
220
|
+
// The arriving view gets its own alpha back: an `opacity` animation of its tracks shows over it from the start, in
|
|
221
|
+
// the same transaction.
|
|
222
|
+
if (_arriving) {
|
|
192
223
|
_arriving.alpha = _arrivingAlpha;
|
|
193
224
|
}
|
|
194
225
|
if (arrivingTracks.hasTracks) {
|
|
@@ -197,7 +228,7 @@ static void ANNUpdateRootLayoutOf(UIViewController *node) {
|
|
|
197
228
|
if (leavingTracks.hasTracks) {
|
|
198
229
|
[clients addObject:leavingTracks];
|
|
199
230
|
}
|
|
200
|
-
// Each element plays on its own: one whose
|
|
231
|
+
// Each element plays on its own: one whose animations fail goes back and stays, the others and the screens go on.
|
|
201
232
|
for (ANNElementTransition *element in elements) {
|
|
202
233
|
[clients addObject:[[ANNIsolatedElement alloc] initWithClient:element name:element.name]];
|
|
203
234
|
}
|
|
@@ -210,6 +241,13 @@ static void ANNUpdateRootLayoutOf(UIViewController *node) {
|
|
|
210
241
|
}
|
|
211
242
|
__weak ANNTransitionAnimator *weakSelf = self;
|
|
212
243
|
_driver = [[ANNFrameDriver alloc] initWithClients:clients length:_plan.length];
|
|
244
|
+
if (_followsGesture) {
|
|
245
|
+
[_driver startPausedOn:container.layer
|
|
246
|
+
done:^(BOOL finished) {
|
|
247
|
+
[weakSelf complete];
|
|
248
|
+
}];
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
213
251
|
[_driver startWithDone:^{
|
|
214
252
|
[weakSelf complete];
|
|
215
253
|
}];
|
|
@@ -288,8 +326,16 @@ static BOOL ANNSideTilts(ANNViewAnimationOptions *side) {
|
|
|
288
326
|
}
|
|
289
327
|
[_wait cancel];
|
|
290
328
|
_wait = nil;
|
|
329
|
+
// An interactive transition ends at its end state too: UIKit completes only one whose end was chosen.
|
|
330
|
+
if (_followsGesture && !_endChosen) {
|
|
331
|
+
_endChosen = YES;
|
|
332
|
+
[_context finishInteractiveTransition];
|
|
333
|
+
}
|
|
334
|
+
// A stopped clock gets its time back in the commit that removes the animations.
|
|
335
|
+
[CATransaction begin];
|
|
291
336
|
[_driver cancel];
|
|
292
337
|
[self complete];
|
|
338
|
+
[CATransaction commit];
|
|
293
339
|
}
|
|
294
340
|
|
|
295
341
|
@end
|
|
@@ -19,8 +19,8 @@ typedef NS_ENUM(NSInteger, ANNTransitionKind) {
|
|
|
19
19
|
ANNTransitionKindDismiss,
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
/// The screen fade of a transition with elements and no screen animation of its own,
|
|
23
|
-
/// nothing to play.
|
|
22
|
+
/// The screen fade of a transition with elements and no screen animation of its own, of a dismissed popover with
|
|
23
|
+
/// nothing to play, or of any transition while the system's Reduce Motion is on (then nothing else plays).
|
|
24
24
|
typedef NS_ENUM(NSInteger, ANNScreenFade) {
|
|
25
25
|
ANNScreenFadeNone,
|
|
26
26
|
/// The arriving screen from 0 to its alpha.
|
|
@@ -43,6 +43,16 @@ static const NSTimeInterval ANNElementsScreenFade = 0.3;
|
|
|
43
43
|
length = MAX(length, [ANNAnimationRules lengthOfElements:_elements shared:_sharedElements]);
|
|
44
44
|
length = MAX(length, _screenFadeDuration);
|
|
45
45
|
_length = length;
|
|
46
|
+
// With the system's Reduce Motion on, nothing moves: the transition is a fade of the same length, the arriving
|
|
47
|
+
// screen in going forward, the leaving one out otherwise.
|
|
48
|
+
if (UIAccessibilityIsReduceMotionEnabled() && length > 0) {
|
|
49
|
+
_enter = nil;
|
|
50
|
+
_exit = nil;
|
|
51
|
+
_elements = @[];
|
|
52
|
+
_sharedElements = @[];
|
|
53
|
+
_screenFade = _isForward ? ANNScreenFadeArrivingIn : ANNScreenFadeLeavingOut;
|
|
54
|
+
_screenFadeDuration = length;
|
|
55
|
+
}
|
|
46
56
|
}
|
|
47
57
|
return self;
|
|
48
58
|
}
|
|
@@ -69,8 +69,8 @@ FOUNDATION_EXPORT BOOL ANNElementStep(NSString *element, NS_NOESCAPE dispatch_bl
|
|
|
69
69
|
/// The line in the log for the element named `element`, skipped for `reason`: it shows and hides with its screen.
|
|
70
70
|
FOUNDATION_EXPORT void ANNElementSkipped(NSString *element, NSString *reason);
|
|
71
71
|
|
|
72
|
-
/// An element's
|
|
73
|
-
/// (`-restore`)
|
|
72
|
+
/// An element's animations, made in `ANNElementStep`: when making them fails, the element's values go back
|
|
73
|
+
/// (`-restore`) and it doesn't move; the rest of the transition plays.
|
|
74
74
|
@interface ANNIsolatedElement : NSObject <ANNFrameClient>
|
|
75
75
|
|
|
76
76
|
- (instancetype)initWithClient:(id<ANNFrameClient>)client
|
|
@@ -182,13 +182,13 @@ BOOL ANNElementStep(NSString *element, NS_NOESCAPE dispatch_block_t work) {
|
|
|
182
182
|
|
|
183
183
|
#pragma mark - ANNFrameClient
|
|
184
184
|
|
|
185
|
-
- (void)
|
|
185
|
+
- (void)addAnimationsWithLength:(NSTimeInterval)length {
|
|
186
186
|
if (_failed) {
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
id<ANNFrameClient> client = _client;
|
|
190
190
|
if (!ANNElementStep(_name, ^{
|
|
191
|
-
[client
|
|
191
|
+
[client addAnimationsWithLength:length];
|
|
192
192
|
})) {
|
|
193
193
|
_failed = YES;
|
|
194
194
|
[self restore];
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
8
8
|
|
|
9
|
-
/// One `elementTransitions[]` item on its React view:
|
|
10
|
-
///
|
|
11
|
-
/// the React view
|
|
9
|
+
/// One `elementTransitions[]` item on its React view: its `transform` and `opacity` animate in place, its parents clip
|
|
10
|
+
/// it as usual; at the end, at a cancel and at a destroy the animations go and the view shows its own values. Nothing
|
|
11
|
+
/// of the React view is written (no value, no frame, no subview, never `layer.zPosition`).
|
|
12
12
|
@interface ANNElementTransition : NSObject <ANNFrameClient>
|
|
13
13
|
|
|
14
14
|
/// The items found: each `id` first in the leaving screen's React view, then in the arriving one; an item not found or
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
|
|
59
59
|
#pragma mark - ANNFrameClient
|
|
60
60
|
|
|
61
|
-
- (void)
|
|
62
|
-
[_tracks
|
|
61
|
+
- (void)addAnimationsWithLength:(NSTimeInterval)length {
|
|
62
|
+
[_tracks addAnimationsWithLength:length];
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
- (NSTimeInterval)endTime {
|