@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
|
@@ -45,8 +45,9 @@ static UIWindowScene *ANNConnectedWindowScene(void) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// Once, on the main queue: the generated native screens before any other part, then the core's parts, the
|
|
49
|
-
// replacement before any React content, the window with the copy of the launch screen, and the
|
|
48
|
+
// Once, on the main queue: the generated native screens before any other part, then the core's parts, the queue of the
|
|
49
|
+
// links, the `<Modal>` replacement before any React content, the window with the copy of the launch screen, and the
|
|
50
|
+
// watch of the bundles.
|
|
50
51
|
+ (void)bootstrapWithHost:(RCTHost *)host {
|
|
51
52
|
ANNAssertMainQueue();
|
|
52
53
|
if (ANNBootstrapped) {
|
|
@@ -55,6 +56,9 @@ static UIWindowScene *ANNConnectedWindowScene(void) {
|
|
|
55
56
|
ANNBootstrapped = YES;
|
|
56
57
|
[self registerGeneratedScreens];
|
|
57
58
|
ANNHost = host;
|
|
59
|
+
// The links that come before React content wait for it; the launch link waits for the scene. Here, not in
|
|
60
|
+
// `ANNAppDelegate`, so an app delegate of the app's own keeps its launch link.
|
|
61
|
+
[ANNDeepLinkQueue start];
|
|
58
62
|
// React Native lets a class with the descriptor of `ModalHostView` stand in for its own.
|
|
59
63
|
[ANNModalHostViewComponentView registerForModalHostView];
|
|
60
64
|
[self makeMainWindowInScene:nil];
|
|
@@ -238,13 +238,7 @@ static NSMutableArray<ANNOverlay *> *ANNOverlays;
|
|
|
238
238
|
[self dismiss:shown answer:nil];
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
-
|
|
242
|
-
UIWindowScene *scene = mainWindow.windowScene;
|
|
243
|
-
// `initWithFrame:` is deprecated from iOS 26 on and warns only with a deployment target from 26: a window
|
|
244
|
-
// without a scene is made so, and joins the scene as the main window gets it.
|
|
245
|
-
ANNOverlayWindow *window =
|
|
246
|
-
scene ? [[ANNOverlayWindow alloc] initWithWindowScene:scene]
|
|
247
|
-
: [[ANNOverlayWindow alloc] initWithFrame:ANNUtils.mainScreen.bounds];
|
|
241
|
+
ANNOverlayWindow *window = [ANNUtils makeWindowOfClass:ANNOverlayWindow.class];
|
|
248
242
|
window.windowLevel = UIWindowLevelNormal;
|
|
249
243
|
window.backgroundColor = UIColor.clearColor;
|
|
250
244
|
window.previousWindow = [self keyWindow];
|
|
@@ -83,6 +83,10 @@ typedef NS_ENUM(NSInteger, ANNRootShowState) {
|
|
|
83
83
|
@property(nonatomic, strong, nullable) ANNFrameDriver *driver;
|
|
84
84
|
@property(nonatomic, copy, nullable) NSArray<ANNViewTracks *> *tracks;
|
|
85
85
|
@property(nonatomic) BOOL animating;
|
|
86
|
+
// The new root's view takes no touches while it animates, and whether it took them before: Core Animation draws it
|
|
87
|
+
// elsewhere than where it is (the view itself stays in place), so a tap on the old screen would reach it.
|
|
88
|
+
@property(nonatomic, weak, nullable) UIView *untouchableRoot;
|
|
89
|
+
@property(nonatomic) BOOL rootTouchesBefore;
|
|
86
90
|
@property(nonatomic) BOOL layersShown;
|
|
87
91
|
@end
|
|
88
92
|
|
|
@@ -533,20 +537,43 @@ static __weak ANNRootShow *ANNAnimatingShow;
|
|
|
533
537
|
NSMutableArray<ANNViewTracks *> *tracks = [NSMutableArray new];
|
|
534
538
|
ANNViewAnimationOptions *enter = [ANNAnimationRules playingSide:animation.enter];
|
|
535
539
|
ANNViewAnimationOptions *exit = [ANNAnimationRules playingSide:animation.exit];
|
|
536
|
-
if (enter && rootView) {
|
|
537
|
-
[tracks addObject:[[ANNViewTracks alloc] initWithView:rootView
|
|
538
|
-
side:enter
|
|
539
|
-
alphaBefore:rootView.alpha]];
|
|
540
|
-
}
|
|
541
|
-
if (exit && snapshot) {
|
|
542
|
-
[tracks addObject:[[ANNViewTracks alloc] initWithView:snapshot
|
|
543
|
-
side:exit
|
|
544
|
-
alphaBefore:snapshot.alpha]];
|
|
545
|
-
}
|
|
546
540
|
NSTimeInterval length =
|
|
547
541
|
MAX([ANNAnimationRules lengthOfSide:enter], [ANNAnimationRules lengthOfSide:exit]);
|
|
542
|
+
if (UIAccessibilityIsReduceMotionEnabled() && length > 0) {
|
|
543
|
+
// With the system's Reduce Motion on, nothing moves: the new root fades in over the snapshot in the same
|
|
544
|
+
// length, or the snapshot fades out without a new root's view.
|
|
545
|
+
if (rootView) {
|
|
546
|
+
[tracks addObject:[[ANNViewTracks alloc] initWithView:rootView
|
|
547
|
+
fadeFrom:0
|
|
548
|
+
to:rootView.alpha
|
|
549
|
+
duration:length
|
|
550
|
+
alphaBefore:rootView.alpha]];
|
|
551
|
+
} else if (snapshot) {
|
|
552
|
+
[tracks addObject:[[ANNViewTracks alloc] initWithView:snapshot
|
|
553
|
+
fadeFrom:snapshot.alpha
|
|
554
|
+
to:0
|
|
555
|
+
duration:length
|
|
556
|
+
alphaBefore:snapshot.alpha]];
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
if (enter && rootView) {
|
|
560
|
+
[tracks addObject:[[ANNViewTracks alloc] initWithView:rootView
|
|
561
|
+
side:enter
|
|
562
|
+
alphaBefore:rootView.alpha]];
|
|
563
|
+
}
|
|
564
|
+
if (exit && snapshot) {
|
|
565
|
+
[tracks addObject:[[ANNViewTracks alloc] initWithView:snapshot
|
|
566
|
+
side:exit
|
|
567
|
+
alphaBefore:snapshot.alpha]];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
548
570
|
show.tracks = tracks;
|
|
549
571
|
show.animating = YES;
|
|
572
|
+
if (rootView) {
|
|
573
|
+
show.untouchableRoot = rootView;
|
|
574
|
+
show.rootTouchesBefore = rootView.userInteractionEnabled;
|
|
575
|
+
rootView.userInteractionEnabled = NO;
|
|
576
|
+
}
|
|
550
577
|
ANNAnimatingShow = show;
|
|
551
578
|
show.driver = [[ANNFrameDriver alloc] initWithClients:tracks length:length];
|
|
552
579
|
// The layers' state from here on: the chain may start and a newer `setRoot` stops it.
|
|
@@ -583,8 +610,8 @@ static __weak ANNRootShow *ANNAnimatingShow;
|
|
|
583
610
|
[self finishLayersOf:show];
|
|
584
611
|
}
|
|
585
612
|
|
|
586
|
-
// The animation stops
|
|
587
|
-
// had and the snapshot goes.
|
|
613
|
+
// The animation stops (its end, a newer `setRoot`, a reload, a failure): its animations go, the views get back what
|
|
614
|
+
// they had and the snapshot goes.
|
|
588
615
|
+ (void)stopAnimationOf:(ANNRootShow *)show {
|
|
589
616
|
if (!show.animating) {
|
|
590
617
|
return;
|
|
@@ -592,13 +619,16 @@ static __weak ANNRootShow *ANNAnimatingShow;
|
|
|
592
619
|
show.animating = NO;
|
|
593
620
|
[show.driver cancel];
|
|
594
621
|
show.driver = nil;
|
|
622
|
+
show.untouchableRoot.userInteractionEnabled = show.rootTouchesBefore;
|
|
623
|
+
show.untouchableRoot = nil;
|
|
595
624
|
for (ANNViewTracks *tracks in show.tracks) {
|
|
596
625
|
[tracks restore];
|
|
597
626
|
}
|
|
598
627
|
show.tracks = nil;
|
|
599
|
-
// UIKit works out the safe area of the window's root view from where the view is
|
|
600
|
-
//
|
|
601
|
-
//
|
|
628
|
+
// UIKit works out the safe area of the window's root view from where the view is: moved away from the top edge by
|
|
629
|
+
// a transform it has no top inset, and putting the transform back doesn't make UIKit work it out again. `enter`
|
|
630
|
+
// moves only what Core Animation draws now; the layout stays, so the root, its bars and its screens end with the
|
|
631
|
+
// window's safe area whatever moved them.
|
|
602
632
|
UIView *rootView = show.root.viewIfLoaded;
|
|
603
633
|
[rootView setNeedsLayout];
|
|
604
634
|
[rootView layoutIfNeeded];
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#import "ANNScreenRegions.h"
|
|
15
15
|
#import "ANNScreenSafeArea.h"
|
|
16
16
|
#import "ANNStackController.h"
|
|
17
|
+
#import "ANNSurfaceHostingView.h"
|
|
17
18
|
#import "ANNSystemUI.h"
|
|
18
19
|
#import "ANNTopBarPresenter.h"
|
|
19
20
|
#import "ANNUtils.h"
|
|
@@ -103,10 +104,12 @@
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// UIKit names the size transition as the moment the status bar's frame changes (a rotation, a new window size): the
|
|
106
|
-
// blur follows it along the animation and takes the final frame at its end.
|
|
107
|
+
// blur follows it along the animation and takes the final frame at its end. The React content takes its new size
|
|
108
|
+
// without animation meanwhile, or what it draws itself is stretched along the animation (`ANNSurfaceHostingView`).
|
|
107
109
|
- (void)viewWillTransitionToSize:(CGSize)size
|
|
108
110
|
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
109
111
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
112
|
+
[ANNSurfaceHostingView followSizeTransition:coordinator];
|
|
110
113
|
__weak ANNComponentViewController *weakSelf = self;
|
|
111
114
|
[coordinator
|
|
112
115
|
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
@@ -12,13 +12,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
12
12
|
from:(UIViewController *)from
|
|
13
13
|
to:(UIViewController *)to;
|
|
14
14
|
|
|
15
|
+
/// The interaction of the swipe back that plays `animator`; nil for any other transition.
|
|
16
|
+
- (nullable id<UIViewControllerInteractiveTransitioning>)interactionForAnimator:
|
|
17
|
+
(id<UIViewControllerAnimatedTransitioning>)animator;
|
|
18
|
+
|
|
15
19
|
@end
|
|
16
20
|
|
|
17
21
|
/// The stack's delegate while a custom animation may be needed: it answers what the stack answers as its own delegate —
|
|
18
22
|
/// every method the stack implements goes to the stack — and also the choice of the transition object. A delegate with
|
|
19
23
|
/// that method turns the iOS 26 content swipe off, so the stack sets this one only while the top's `animations.pop`,
|
|
20
|
-
/// the pushed screen's `animations.push` or a running `setStackRoot` is its own, and itself otherwise.
|
|
21
|
-
///
|
|
24
|
+
/// the pushed screen's `animations.push` or a running `setStackRoot` is its own, and itself otherwise. An interaction
|
|
25
|
+
/// controller only while `answersInteraction`: the stack's own swipe back then plays the top's own pop under the
|
|
26
|
+
/// finger; otherwise UIKit doesn't see the method, and the swipe stays UIKit's.
|
|
22
27
|
@interface ANNStackAnimatingDelegate : NSObject <UINavigationControllerDelegate>
|
|
23
28
|
|
|
24
29
|
- (instancetype)initWithStack:(UINavigationController<ANNStackAnimating> *)stack
|
|
@@ -27,6 +32,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
27
32
|
- (instancetype)init NS_UNAVAILABLE;
|
|
28
33
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
29
34
|
|
|
35
|
+
/// The top's pop follows the swipe back (`animations.pop.interactive`). UIKit reads the delegate's methods when it is
|
|
36
|
+
/// set: the stack sets the delegate again after a change.
|
|
37
|
+
@property(nonatomic) BOOL answersInteraction;
|
|
38
|
+
|
|
30
39
|
@end
|
|
31
40
|
|
|
32
41
|
NS_ASSUME_NONNULL_END
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
return self;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
// The choice of the transition object is this delegate's own; the interaction controller
|
|
16
|
-
// methods of `UINavigationControllerDelegate` are the stack's when the
|
|
17
|
-
// forwarded (design 11).
|
|
15
|
+
// The choice of the transition object is this delegate's own; the interaction controller too, answered only while
|
|
16
|
+
// the top's pop follows the swipe. The other methods of `UINavigationControllerDelegate` are the stack's when the
|
|
17
|
+
// stack has them; nothing outside the protocol is forwarded (design 11).
|
|
18
18
|
static BOOL ANNIsOwnSelector(SEL selector) {
|
|
19
19
|
SEL animation = @selector(navigationController:
|
|
20
20
|
animationControllerForOperation:fromViewController:toViewController:);
|
|
21
21
|
return selector == animation;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
static BOOL
|
|
24
|
+
static BOOL ANNIsInteractionSelector(SEL selector) {
|
|
25
25
|
return selector == @selector(navigationController:interactionControllerForAnimationController:);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -33,7 +33,7 @@ static BOOL ANNIsDelegateSelector(SEL selector) {
|
|
|
33
33
|
|
|
34
34
|
// A method of the protocol this delegate passes to the stack.
|
|
35
35
|
- (BOOL)forwardsToStack:(SEL)selector {
|
|
36
|
-
return !ANNIsOwnSelector(selector) && !
|
|
36
|
+
return !ANNIsOwnSelector(selector) && !ANNIsInteractionSelector(selector) &&
|
|
37
37
|
ANNIsDelegateSelector(selector) && [_stack respondsToSelector:selector];
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -41,8 +41,8 @@ static BOOL ANNIsDelegateSelector(SEL selector) {
|
|
|
41
41
|
if (ANNIsOwnSelector(selector)) {
|
|
42
42
|
return YES;
|
|
43
43
|
}
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
44
|
+
if (ANNIsInteractionSelector(selector)) {
|
|
45
|
+
return _answersInteraction;
|
|
46
46
|
}
|
|
47
47
|
if ([super respondsToSelector:selector]) {
|
|
48
48
|
return YES;
|
|
@@ -89,4 +89,11 @@ static BOOL ANNIsDelegateSelector(SEL selector) {
|
|
|
89
89
|
return [_stack animatorForOperation:operation from:fromVC to:toVC];
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
- (id<UIViewControllerInteractiveTransitioning>)
|
|
93
|
+
navigationController:(UINavigationController *)navigationController
|
|
94
|
+
interactionControllerForAnimationController:
|
|
95
|
+
(id<UIViewControllerAnimatedTransitioning>)animationController {
|
|
96
|
+
return [_stack interactionForAnimator:animationController];
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
@end
|
|
@@ -93,6 +93,14 @@ FOUNDATION_EXPORT NSString *_Nullable ANNStackKeyOfLayout(ANNLayoutNode *node);
|
|
|
93
93
|
/// it), or UIKit's More navigation shows the stack's first screen only (its swipe goes back to the More list).
|
|
94
94
|
- (BOOL)swipeBackIsUIKits:(UIGestureRecognizer *)recognizer;
|
|
95
95
|
|
|
96
|
+
/// The swipe of `recognizer` may play the pop: the stack's own swipe (from the leading edge, toward the trailing side)
|
|
97
|
+
/// only while the top's pop follows the finger (`animations.pop.interactive`) and the screen under is in place, UIKit's
|
|
98
|
+
/// swipes of the stack's navigation only while it doesn't. Every other swipe may.
|
|
99
|
+
- (BOOL)swipeBackTakes:(UIGestureRecognizer *)recognizer;
|
|
100
|
+
|
|
101
|
+
/// `recognizer` is the stack's own swipe back, not one of UIKit's.
|
|
102
|
+
- (BOOL)swipeBackIsStacks:(UIGestureRecognizer *)recognizer;
|
|
103
|
+
|
|
96
104
|
/// The stack has one entry: there is nowhere to go back to.
|
|
97
105
|
@property(nonatomic, readonly) BOOL hasOneEntry;
|
|
98
106
|
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
#import "ANNSheetPresenter.h"
|
|
13
13
|
#import "ANNShownScreens.h"
|
|
14
14
|
#import "ANNStackAnimatingDelegate.h"
|
|
15
|
+
#import "ANNStackPopAnimationOptions.h"
|
|
15
16
|
#import "ANNStackPresenter.h"
|
|
17
|
+
#import "ANNSwipePopInteraction.h"
|
|
16
18
|
#import "ANNSystemUI.h"
|
|
17
19
|
#import "ANNTopBarPresenter.h"
|
|
18
20
|
#import "ANNTransitionAnimator.h"
|
|
@@ -54,6 +56,10 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
54
56
|
return state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
// How far from the leading edge of the stack's view the stack's own swipe back starts, in points: the strip the side
|
|
60
|
+
// menu leaves to the swipe back (`ANNSideMenuEdgeWidth`).
|
|
61
|
+
static const CGFloat ANNOwnSwipeEdgeWidth = 20;
|
|
62
|
+
|
|
57
63
|
@interface ANNStackController () <ANNStackDisplay, ANNStackAnimating>
|
|
58
64
|
@end
|
|
59
65
|
|
|
@@ -90,6 +96,12 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
90
96
|
ANNTransitionPlan *_pendingPlan;
|
|
91
97
|
ANNStackChange _pendingChange;
|
|
92
98
|
__weak ANNTransitionAnimator *_runningAnimator;
|
|
99
|
+
// The interaction of the swipe back that plays the top's own pop, until UIKit asks for it.
|
|
100
|
+
__weak ANNSwipePopInteraction *_swipeInteraction;
|
|
101
|
+
// The stack's own swipe from the leading edge, on while the top's pop follows the finger: UIKit runs its swipe
|
|
102
|
+
// without asking the delegate for a transition, so that one would only slide.
|
|
103
|
+
UIPanGestureRecognizer *_ownSwipe;
|
|
104
|
+
InteractivePopGestureDelegate *_ownSwipeDelegate;
|
|
93
105
|
BOOL _gateUpdateScheduled;
|
|
94
106
|
// The stack is made (its helper set up): its options can be resolved.
|
|
95
107
|
BOOL _madeUp;
|
|
@@ -260,6 +272,16 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
260
272
|
content:YES
|
|
261
273
|
barSwipe:barSwipe];
|
|
262
274
|
}
|
|
275
|
+
if (!_ownSwipe) {
|
|
276
|
+
_ownSwipe = [[UIPanGestureRecognizer alloc] initWithTarget:self
|
|
277
|
+
action:@selector(ownSwipeBack:)];
|
|
278
|
+
_ownSwipe.enabled = _animatingDelegate.answersInteraction;
|
|
279
|
+
[self.view addGestureRecognizer:_ownSwipe];
|
|
280
|
+
}
|
|
281
|
+
_ownSwipeDelegate = [InteractivePopGestureDelegate installOn:_ownSwipe
|
|
282
|
+
stack:self
|
|
283
|
+
content:NO
|
|
284
|
+
barSwipe:barSwipe];
|
|
263
285
|
[self updateSwipeBack];
|
|
264
286
|
}
|
|
265
287
|
|
|
@@ -520,6 +542,9 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
520
542
|
|
|
521
543
|
- (BOOL)isSwipingBack {
|
|
522
544
|
UINavigationController *navigation = self.shownNavigationController;
|
|
545
|
+
if (navigation == self && ANNIsActive(_ownSwipe)) {
|
|
546
|
+
return YES;
|
|
547
|
+
}
|
|
523
548
|
if (ANNIsActive(navigation.interactivePopGestureRecognizer)) {
|
|
524
549
|
return YES;
|
|
525
550
|
}
|
|
@@ -813,15 +838,20 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
813
838
|
#pragma mark - The custom transitions
|
|
814
839
|
|
|
815
840
|
// UIKit asks while it starts the change the queue asked for: the queue's plan when the operation and the arriving
|
|
816
|
-
// entry are the plan's (the back button and the commands go through the queue, so its plan covers them);
|
|
817
|
-
//
|
|
841
|
+
// entry are the plan's (the back button and the commands go through the queue, so its plan covers them); the swipe
|
|
842
|
+
// back gets the top's own pop only with `animations.pop.interactive` (`swipeAnimatorFrom:to:`); nil for anything
|
|
843
|
+
// else — UIKit's own pops, a zoom alone.
|
|
818
844
|
- (id<UIViewControllerAnimatedTransitioning>)
|
|
819
845
|
animatorForOperation:(UINavigationControllerOperation)operation
|
|
820
846
|
from:(UIViewController *)from
|
|
821
847
|
to:(UIViewController *)to {
|
|
848
|
+
if ([self isSwipingBack]) {
|
|
849
|
+
return operation == UINavigationControllerOperationPop
|
|
850
|
+
? [self swipeAnimatorFrom:from to:to]
|
|
851
|
+
: nil;
|
|
852
|
+
}
|
|
822
853
|
ANNTransitionPlan *plan = _pendingPlan;
|
|
823
|
-
if (!plan || operation == UINavigationControllerOperationNone || to != plan.toNode
|
|
824
|
-
[self isSwipingBack]) {
|
|
854
|
+
if (!plan || operation == UINavigationControllerOperationNone || to != plan.toNode) {
|
|
825
855
|
return nil;
|
|
826
856
|
}
|
|
827
857
|
BOOL matches = _pendingChange == ANNStackChangeReplace ||
|
|
@@ -838,16 +868,73 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
838
868
|
return animator;
|
|
839
869
|
}
|
|
840
870
|
|
|
871
|
+
// The stack's own swipe back of a top whose pop follows the finger (`animations.pop.interactive`, not
|
|
872
|
+
// `enabled: false`): the pop plays under it. Nil otherwise, also when UIKit won't ask for the interaction (the gate
|
|
873
|
+
// hasn't seen the option yet: a change of the default options) or the screen under hasn't settled
|
|
874
|
+
// (`revealedEntrySettled:`): UIKit asks for the transition before the queue takes the swipe and moves those, and the
|
|
875
|
+
// frames take the views as they are now.
|
|
876
|
+
- (nullable ANNTransitionAnimator *)swipeAnimatorFrom:(UIViewController *)from
|
|
877
|
+
to:(UIViewController *)to {
|
|
878
|
+
if (!ANNIsActive(_ownSwipe) || !_animatingDelegate.answersInteraction ||
|
|
879
|
+
![self isOwnViewController:from] || ![self revealedEntrySettled:to]) {
|
|
880
|
+
return nil;
|
|
881
|
+
}
|
|
882
|
+
ANNStackPopAnimationOptions *pop = [from resolveOptionsWithDefault].animations.pop;
|
|
883
|
+
if (![ANNStackController popFollowsSwipe:pop]) {
|
|
884
|
+
return nil;
|
|
885
|
+
}
|
|
886
|
+
ANNTransitionPlan *plan = [ANNTransitionPlan stackPlanOfKind:ANNTransitionKindPop
|
|
887
|
+
animation:pop
|
|
888
|
+
waitForRender:nil
|
|
889
|
+
waitLimit:0
|
|
890
|
+
from:from
|
|
891
|
+
to:to];
|
|
892
|
+
if (!plan) {
|
|
893
|
+
return nil;
|
|
894
|
+
}
|
|
895
|
+
ANNTransitionAnimator *animator = [[ANNTransitionAnimator alloc] initWithPlan:plan];
|
|
896
|
+
// The interaction keeps itself while it follows the gesture; UIKit keeps it once it starts it.
|
|
897
|
+
ANNSwipePopInteraction *interaction =
|
|
898
|
+
[[ANNSwipePopInteraction alloc] initWithAnimator:animator recognizer:_ownSwipe view:self.view];
|
|
899
|
+
_swipeInteraction = interaction;
|
|
900
|
+
_runningAnimator = animator;
|
|
901
|
+
return animator;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// The screen under is in place for the frames: its view made, not parked, not rendering as a lower screen.
|
|
905
|
+
- (BOOL)revealedEntrySettled:(nullable UIViewController *)revealed {
|
|
906
|
+
return revealed.isViewLoaded && ![_lowerRendering objectForKey:revealed] &&
|
|
907
|
+
![_lowerToRender containsObject:revealed] &&
|
|
908
|
+
![[self parkedEntries] containsObject:revealed];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
+ (BOOL)popFollowsSwipe:(nullable ANNStackPopAnimationOptions *)pop {
|
|
912
|
+
return [ANNAnimationRules stackIsOwn:pop] && [pop.interactive withDefault:NO] &&
|
|
913
|
+
[pop.enabled withDefault:YES];
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
- (nullable id<UIViewControllerInteractiveTransitioning>)interactionForAnimator:
|
|
917
|
+
(id<UIViewControllerAnimatedTransitioning>)animator {
|
|
918
|
+
ANNSwipePopInteraction *interaction = _swipeInteraction;
|
|
919
|
+
return interaction && interaction.animator == animator ? interaction : nil;
|
|
920
|
+
}
|
|
921
|
+
|
|
841
922
|
// The animating delegate while a custom animation may be needed: a change with its own plan now, or a top whose
|
|
842
923
|
// `animations.pop` is its own; the stack itself otherwise. UIKit reads the delegate's methods when it is set, so it is
|
|
843
|
-
// set again at each change
|
|
924
|
+
// set again at each change, and again when the interaction of the swipe comes or goes with the top's
|
|
925
|
+
// `animations.pop.interactive`.
|
|
844
926
|
- (void)updateAnimationGateWith:(ANNTransitionPlan *)plan {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
927
|
+
UIViewController *top = [self getCurrentChild];
|
|
928
|
+
ANNStackPopAnimationOptions *pop = top ? [top resolveOptionsWithDefault].animations.pop : nil;
|
|
929
|
+
BOOL needed = plan != nil || [ANNAnimationRules stackIsOwn:pop];
|
|
930
|
+
BOOL interactive = [ANNStackController popFollowsSwipe:pop];
|
|
931
|
+
if (_animatingDelegate.answersInteraction != interactive) {
|
|
932
|
+
_animatingDelegate.answersInteraction = interactive;
|
|
933
|
+
if (self.delegate == _animatingDelegate) {
|
|
934
|
+
self.delegate = self;
|
|
935
|
+
}
|
|
850
936
|
}
|
|
937
|
+
_ownSwipe.enabled = interactive;
|
|
851
938
|
id<UINavigationControllerDelegate> delegate =
|
|
852
939
|
needed ? (id<UINavigationControllerDelegate>)_animatingDelegate : self;
|
|
853
940
|
if (self.delegate != delegate) {
|
|
@@ -934,13 +1021,66 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
934
1021
|
if (top != nil && top != [self getCurrentChild]) {
|
|
935
1022
|
return YES;
|
|
936
1023
|
}
|
|
937
|
-
BOOL own = recognizer == self.interactivePopGestureRecognizer;
|
|
1024
|
+
BOOL own = recognizer == self.interactivePopGestureRecognizer || recognizer == _ownSwipe;
|
|
938
1025
|
if (@available(iOS 26.0, *)) {
|
|
939
1026
|
own = own || recognizer == self.interactiveContentPopGestureRecognizer;
|
|
940
1027
|
}
|
|
941
1028
|
return !own && _queue.shown.count < 2;
|
|
942
1029
|
}
|
|
943
1030
|
|
|
1031
|
+
- (BOOL)swipeBackIsStacks:(UIGestureRecognizer *)recognizer {
|
|
1032
|
+
return recognizer != nil && recognizer == _ownSwipe;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
- (BOOL)swipeBackTakes:(UIGestureRecognizer *)recognizer {
|
|
1036
|
+
BOOL uikits = recognizer == self.interactivePopGestureRecognizer;
|
|
1037
|
+
if (@available(iOS 26.0, *)) {
|
|
1038
|
+
uikits = uikits || recognizer == self.interactiveContentPopGestureRecognizer;
|
|
1039
|
+
}
|
|
1040
|
+
if (recognizer != _ownSwipe && !uikits) {
|
|
1041
|
+
return YES;
|
|
1042
|
+
}
|
|
1043
|
+
NSArray<UIViewController *> *shown = _queue.shown;
|
|
1044
|
+
UIViewController *revealed = shown.count >= 2 ? shown[shown.count - 2] : nil;
|
|
1045
|
+
// The options as they are now, as `swipeAnimatorFrom:to:` reads them: the gate may lag a change of the defaults,
|
|
1046
|
+
// and a swipe of ours without the pop would be a pop the finger can't hold.
|
|
1047
|
+
UIViewController *top = self.topViewController;
|
|
1048
|
+
BOOL ownPlays = _ownSwipe.enabled && _animatingDelegate.answersInteraction && top &&
|
|
1049
|
+
top == [self getCurrentChild] && [self isOwnViewController:top] &&
|
|
1050
|
+
[ANNStackController
|
|
1051
|
+
popFollowsSwipe:[top resolveOptionsWithDefault].animations.pop] &&
|
|
1052
|
+
[self revealedEntrySettled:revealed];
|
|
1053
|
+
if (uikits) {
|
|
1054
|
+
return !ownPlays;
|
|
1055
|
+
}
|
|
1056
|
+
return ownPlays && [self panStartsAtLeadingEdge:_ownSwipe];
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// A pan that starts near the leading edge of the stack's view and moves more across than along, toward the trailing
|
|
1060
|
+
// side: where UIKit's edge swipe starts.
|
|
1061
|
+
- (BOOL)panStartsAtLeadingEdge:(UIPanGestureRecognizer *)pan {
|
|
1062
|
+
UIView *view = self.view;
|
|
1063
|
+
CGPoint at = [pan locationInView:view];
|
|
1064
|
+
CGPoint move = [pan translationInView:view];
|
|
1065
|
+
BOOL rightToLeft =
|
|
1066
|
+
view.effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
|
|
1067
|
+
CGFloat start = at.x - move.x;
|
|
1068
|
+
CGFloat fromEdge = rightToLeft ? view.bounds.size.width - start : start;
|
|
1069
|
+
if (move.x == 0 && move.y == 0) {
|
|
1070
|
+
move = [pan velocityInView:view];
|
|
1071
|
+
}
|
|
1072
|
+
CGFloat along = rightToLeft ? -move.x : move.x;
|
|
1073
|
+
return fromEdge <= ANNOwnSwipeEdgeWidth && along > fabs(move.y);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// The stack's own swipe began: a pop as UIKit's swipe makes it, which the stack follows as the swipe back
|
|
1077
|
+
// (`followUIKitTransition`); the transition asks for the top's pop and its interaction (`swipeAnimatorFrom:to:`).
|
|
1078
|
+
- (void)ownSwipeBack:(UIPanGestureRecognizer *)recognizer {
|
|
1079
|
+
if (recognizer.state == UIGestureRecognizerStateBegan) {
|
|
1080
|
+
[self popViewControllerAnimated:YES];
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
944
1084
|
- (BOOL)hasOneEntry {
|
|
945
1085
|
return _queue.entries.count < 2;
|
|
946
1086
|
}
|
|
@@ -983,6 +1123,7 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
983
1123
|
BOOL edge = !(isBool && ![value boolValue]);
|
|
984
1124
|
BOOL content = !value || (isBool && [value boolValue]);
|
|
985
1125
|
_edgeSwipeDelegate.enabled = edge;
|
|
1126
|
+
_ownSwipeDelegate.enabled = edge;
|
|
986
1127
|
_moreEdgeSwipeDelegate.enabled = edge;
|
|
987
1128
|
_contentSwipeDelegate.enabled = content;
|
|
988
1129
|
_moreContentSwipeDelegate.enabled = content;
|
|
@@ -992,6 +1133,7 @@ static BOOL ANNIsActive(UIGestureRecognizer *recognizer) {
|
|
|
992
1133
|
- (void)swipeBackFollowsBar {
|
|
993
1134
|
BOOL hidden = [ANNSystemUI barHiddenOf:self.shownNavigationController];
|
|
994
1135
|
_edgeSwipeDelegate.barHidden = hidden;
|
|
1136
|
+
_ownSwipeDelegate.barHidden = hidden;
|
|
995
1137
|
_contentSwipeDelegate.barHidden = hidden;
|
|
996
1138
|
_moreEdgeSwipeDelegate.barHidden = hidden;
|
|
997
1139
|
_moreContentSwipeDelegate.barHidden = hidden;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
@class ANNTransitionAnimator;
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
/// The interaction of a swipe back that plays the screen's own pop (`animations.pop.interactive`). It follows the
|
|
8
|
+
/// stack's own swipe from the leading edge (UIKit runs its swipe without asking the delegate for a transition), and
|
|
9
|
+
/// shows the animator's frames as far as the finger went across `view`, in the direction of the swipe back. Letting go
|
|
10
|
+
/// finishes the pop after a flick back or past half way, and puts the screen back otherwise; the rest plays faster
|
|
11
|
+
/// after a quick flick, never slower, and UIKit ends its part at the same pace. An interaction UIKit never starts goes
|
|
12
|
+
/// with the end of the gesture. Main queue.
|
|
13
|
+
@interface ANNSwipePopInteraction : NSObject <UIViewControllerInteractiveTransitioning>
|
|
14
|
+
|
|
15
|
+
- (instancetype)initWithAnimator:(ANNTransitionAnimator *)animator
|
|
16
|
+
recognizer:(UIPanGestureRecognizer *)recognizer
|
|
17
|
+
view:(UIView *)view NS_DESIGNATED_INITIALIZER;
|
|
18
|
+
|
|
19
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
20
|
+
+ (instancetype)new NS_UNAVAILABLE;
|
|
21
|
+
|
|
22
|
+
@property(nonatomic, weak, readonly, nullable) ANNTransitionAnimator *animator;
|
|
23
|
+
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
NS_ASSUME_NONNULL_END
|