@apexnative/navigation 1.0.0-alpha.4 → 1.0.0-alpha.6
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/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +23 -14
- package/android/CORE.md +32 -19
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +60 -13
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +28 -13
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -51
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +30 -39
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +22 -21
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +119 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +2 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +4 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +4 -2
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +1 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +23 -2
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +36 -0
- package/docs-md/screen-ui/bar-buttons.md +4 -4
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +16 -11
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +1 -1
- package/ios/Animations/ANNFrameDriver.mm +2 -0
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -4
- package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
- package/ios/CLAUDE.md +17 -15
- package/ios/CORE.md +30 -26
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +25 -12
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNNavigationState.h +4 -6
- package/ios/Core/ANNNavigationState.mm +0 -4
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +18 -0
- package/ios/Options/ANNOptions.mm +112 -1
- package/ios/Options/ANNOptionsReader.mm +110 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/Image.h +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +6 -0
- package/ios/React/ANNSurfaceHostingView.mm +116 -0
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +20 -16
- package/lib/module/ARCHITECTURE.md +8 -4
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/places.js +8 -0
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/screens.js +29 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +3 -3
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +2 -0
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +20 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +30 -9
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +8 -5
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +32 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +8 -4
- package/src/api/app/createNavigation.ts +3 -3
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/engine/commands.ts +30 -6
- package/src/engine/places.ts +9 -0
- package/src/engine/screens.tsx +29 -8
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/options/animations.ts +31 -9
- package/src/interfaces/options/bottomTabs.ts +18 -5
- package/src/interfaces/options/buttons.ts +8 -8
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +32 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
#import "ANNPreviewPush.h"
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
|
+
#import "ANNCommandRunner.h"
|
|
4
|
+
#import "ANNComponentViewController.h"
|
|
3
5
|
#import "ANNErrorTexts.h"
|
|
6
|
+
#import "ANNExternalViewController.h"
|
|
4
7
|
#import "ANNLayoutFactory.h"
|
|
5
8
|
#import "ANNParking.h"
|
|
6
9
|
#import "ANNPopoverPresenter.h"
|
|
7
10
|
#import "ANNPreviewInteraction.h"
|
|
8
11
|
#import "ANNReloadCoordinator.h"
|
|
12
|
+
#import "ANNSlotContainers.h"
|
|
13
|
+
#import "ANNSlotScreenView.h"
|
|
9
14
|
#import "ANNStackCommands.h"
|
|
10
15
|
#import "ANNStackController.h"
|
|
11
16
|
#import "ApexNavigation+Internal.h"
|
|
12
17
|
#import "UIViewController+LayoutProtocol.h"
|
|
13
18
|
|
|
14
|
-
// The view of a component mounts a frame or so after the command that rendered it: it is looked for
|
|
15
|
-
// a
|
|
16
|
-
static const
|
|
19
|
+
// The view of a component mounts a frame or so after the command that rendered it: it is looked for at each mount of
|
|
20
|
+
// the source's content until this limit (30 frames), then the preview goes, as with a `nativeID` no view has.
|
|
21
|
+
static const NSTimeInterval ANNPreviewSearchLimit = 30.0 / 60;
|
|
22
|
+
// Where the source's mounts can't be heard, a frame apart.
|
|
17
23
|
static const NSTimeInterval ANNPreviewSearchInterval = 1.0 / 60;
|
|
18
24
|
|
|
19
25
|
// One preview from its push to its end: the tap took it, or it went.
|
|
@@ -28,9 +34,14 @@ static const NSTimeInterval ANNPreviewSearchInterval = 1.0 / 60;
|
|
|
28
34
|
@property(nonatomic, copy) NSString *componentId;
|
|
29
35
|
@property(nonatomic, copy) NSString *what;
|
|
30
36
|
@property(nonatomic) NSUInteger generation;
|
|
31
|
-
|
|
37
|
+
// The content whose mounts the search hears, while it looks.
|
|
38
|
+
@property(nonatomic, weak, nullable) UIView<ANNComponentProtocol> *watched;
|
|
32
39
|
@property(nonatomic) BOOL attached;
|
|
33
40
|
@property(nonatomic) BOOL done;
|
|
41
|
+
// The size of the preview, taken at the push.
|
|
42
|
+
@property(nonatomic) CGSize size;
|
|
43
|
+
// The screen rendered: at the push, or at the press for a screen of its own React root.
|
|
44
|
+
@property(nonatomic) BOOL rendered;
|
|
34
45
|
// The screen's unseen place in the main window, at its preview size, until UIKit takes its view for the menu.
|
|
35
46
|
@property(nonatomic, strong, nullable) UIView *host;
|
|
36
47
|
@property(nonatomic, strong, nullable) ANNParking *parking;
|
|
@@ -48,6 +59,15 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
48
59
|
return records;
|
|
49
60
|
}
|
|
50
61
|
|
|
62
|
+
// A React screen with a root of its own renders at the press: a view with a preview costs no React root, no render and
|
|
63
|
+
// no effects of the screen until the user presses it. A native screen (a kind of component screen) renders as it is
|
|
64
|
+
// made, and a screen of the app's one tree renders in JavaScript as its slot is added: both render at the push.
|
|
65
|
+
static BOOL ANNPreviewRendersAtPress(UIViewController *screen) {
|
|
66
|
+
return [screen isKindOfClass:ANNComponentViewController.class] &&
|
|
67
|
+
![screen isKindOfClass:ANNExternalViewController.class] &&
|
|
68
|
+
![screen.layoutInfo.name hasPrefix:ANNSlotPrefix];
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
@implementation ANNPreviewPush
|
|
52
72
|
|
|
53
73
|
+ (void)push:(ANNLayoutNode *)layout
|
|
@@ -68,6 +88,7 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
68
88
|
ANNPreviewRecord *record = [ANNPreviewRecord new];
|
|
69
89
|
record.previewId = layout.nodeId;
|
|
70
90
|
record.screen = screen;
|
|
91
|
+
record.size = size;
|
|
71
92
|
record.source = source;
|
|
72
93
|
record.answer = answer;
|
|
73
94
|
// The key applies only once the tap made the preview a screen of the stack.
|
|
@@ -78,18 +99,22 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
78
99
|
record.generation = answer.generation;
|
|
79
100
|
ANNPreviewRecords()[record.previewId] = record;
|
|
80
101
|
@try {
|
|
81
|
-
|
|
102
|
+
if (!ANNPreviewRendersAtPress(screen)) {
|
|
103
|
+
[self parkAndRender:record];
|
|
104
|
+
}
|
|
82
105
|
} @catch (NSException *exception) {
|
|
83
106
|
[self finish:record];
|
|
84
107
|
[screen destroy];
|
|
85
108
|
@throw;
|
|
86
109
|
}
|
|
87
|
-
[self
|
|
110
|
+
[self startSearch:record];
|
|
88
111
|
}
|
|
89
112
|
|
|
90
|
-
// The screen renders
|
|
113
|
+
// The screen renders, once: its view waits unseen in the main window, at the size of the preview, so that its content
|
|
91
114
|
// lays out; nothing waits for it.
|
|
92
|
-
+ (void)parkAndRender:(ANNPreviewRecord *)record
|
|
115
|
+
+ (void)parkAndRender:(ANNPreviewRecord *)record {
|
|
116
|
+
record.rendered = YES;
|
|
117
|
+
CGSize size = record.size;
|
|
93
118
|
UIWindow *window = ApexNavigation.mainWindow;
|
|
94
119
|
if (window) {
|
|
95
120
|
UIView *host = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
|
|
@@ -110,42 +135,103 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
110
135
|
|
|
111
136
|
#pragma mark - The search
|
|
112
137
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
138
|
+
// The view is looked for at once; else again at each mount of the source's content, where it comes with one of them,
|
|
139
|
+
// and a last time at the limit, when the preview goes. Both later steps keep the generation they were made with: after
|
|
140
|
+
// a reload they do nothing.
|
|
141
|
+
+ (void)startSearch:(ANNPreviewRecord *)record {
|
|
142
|
+
if ([self search:record last:NO]) {
|
|
116
143
|
return;
|
|
117
144
|
}
|
|
145
|
+
UIViewController *source = record.source;
|
|
146
|
+
// A source that is a container (a stack) is heard through the screen it shows.
|
|
147
|
+
UIView<ANNComponentProtocol> *content =
|
|
148
|
+
source.reactView ?: source.presentedComponentViewController.reactView;
|
|
149
|
+
__weak ANNPreviewRecord *weakRecord = record;
|
|
150
|
+
NSUInteger generation = record.generation;
|
|
151
|
+
// A slot screen hears only the first mount of its slot, not those deeper in it, and a container may show no React
|
|
152
|
+
// content yet: there the view is looked for on each frame until the limit.
|
|
153
|
+
if (!content || [content isKindOfClass:ANNSlotScreenView.class]) {
|
|
154
|
+
[self searchNextFrame:record];
|
|
155
|
+
} else {
|
|
156
|
+
record.watched = content;
|
|
157
|
+
[content setContentDidMount:^{
|
|
158
|
+
ANNPreviewRecord *searching = weakRecord;
|
|
159
|
+
if (searching && generation == ANNReloadCoordinator.generation) {
|
|
160
|
+
[ANNPreviewPush search:searching last:NO];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
forKey:[self mountKeyOf:record]];
|
|
164
|
+
}
|
|
165
|
+
int64_t delay = (int64_t)(ANNPreviewSearchLimit * NSEC_PER_SEC);
|
|
166
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), ^{
|
|
167
|
+
ANNPreviewRecord *searching = weakRecord;
|
|
168
|
+
if (searching && generation == ANNReloadCoordinator.generation) {
|
|
169
|
+
[ANNPreviewPush search:searching last:YES];
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// One more search on the next frame, and so on until the view is found or the preview goes; after a reload, none.
|
|
175
|
+
+ (void)searchNextFrame:(ANNPreviewRecord *)record {
|
|
176
|
+
__weak ANNPreviewRecord *weakRecord = record;
|
|
177
|
+
NSUInteger generation = record.generation;
|
|
178
|
+
int64_t delay = (int64_t)(ANNPreviewSearchInterval * NSEC_PER_SEC);
|
|
179
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), ^{
|
|
180
|
+
ANNPreviewRecord *searching = weakRecord;
|
|
181
|
+
if (searching && generation == ANNReloadCoordinator.generation &&
|
|
182
|
+
![ANNPreviewPush search:searching last:NO]) {
|
|
183
|
+
[ANNPreviewPush searchNextFrame:searching];
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// YES when the search is over: the view was found, or the preview went or stopped. Each search asks first whether the
|
|
189
|
+
// preview still lives: a source that went makes it go, a reload stops it silently. The last one, not finding the view,
|
|
190
|
+
// makes the preview go.
|
|
191
|
+
+ (BOOL)search:(ANNPreviewRecord *)record last:(BOOL)last {
|
|
192
|
+
if (record.done || record.attached) {
|
|
193
|
+
return YES;
|
|
194
|
+
}
|
|
118
195
|
if (record.generation != ANNReloadCoordinator.generation) {
|
|
119
|
-
return;
|
|
196
|
+
return YES;
|
|
120
197
|
}
|
|
121
198
|
UIViewController *source = record.source;
|
|
122
199
|
if (!source || source.nodeHelper.destroyed) {
|
|
123
200
|
[self gone:record];
|
|
124
|
-
return;
|
|
201
|
+
return YES;
|
|
125
202
|
}
|
|
126
203
|
// Read only: the view is found by its `nativeID`, and its tag is its current Fabric tag.
|
|
127
204
|
UIView *view = [ANNPopoverPresenter viewWithNativeID:record.nativeID in:source.viewIfLoaded];
|
|
128
205
|
if (view) {
|
|
129
206
|
[self attach:record toView:view source:source];
|
|
130
|
-
return;
|
|
207
|
+
return YES;
|
|
131
208
|
}
|
|
132
|
-
if (
|
|
209
|
+
if (last) {
|
|
133
210
|
[self gone:record];
|
|
134
|
-
return;
|
|
211
|
+
return YES;
|
|
135
212
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
213
|
+
return NO;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// The key of the search's mount handler: one per preview, so that two previews of one source don't replace each other.
|
|
217
|
+
+ (NSString *)mountKeyOf:(ANNPreviewRecord *)record {
|
|
218
|
+
return [@"preview." stringByAppendingString:record.previewId];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The search stops hearing the mounts: only its own handler goes.
|
|
222
|
+
+ (void)stopWatching:(ANNPreviewRecord *)record {
|
|
223
|
+
UIView<ANNComponentProtocol> *content = record.watched;
|
|
224
|
+
record.watched = nil;
|
|
225
|
+
[content setContentDidMount:nil forKey:[self mountKeyOf:record]];
|
|
144
226
|
}
|
|
145
227
|
|
|
146
228
|
+ (void)attach:(ANNPreviewRecord *)record toView:(UIView *)view source:(UIViewController *)source {
|
|
147
229
|
record.attached = YES;
|
|
230
|
+
[self stopWatching:record];
|
|
148
231
|
__weak ANNPreviewRecord *weakRecord = record;
|
|
232
|
+
BOOL (^prepare)(void) = ^BOOL {
|
|
233
|
+
return [ANNPreviewPush renderAtPress:weakRecord];
|
|
234
|
+
};
|
|
149
235
|
BOOL (^commit)(void) = ^BOOL {
|
|
150
236
|
return [ANNPreviewPush commit:weakRecord];
|
|
151
237
|
};
|
|
@@ -156,10 +242,35 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
156
242
|
toView:view
|
|
157
243
|
reactTag:view.tag
|
|
158
244
|
source:source
|
|
245
|
+
prepare:prepare
|
|
159
246
|
commit:commit
|
|
160
247
|
dismissed:dismissed];
|
|
161
248
|
}
|
|
162
249
|
|
|
250
|
+
#pragma mark - The press
|
|
251
|
+
|
|
252
|
+
// The press starts (UIKit asks for the menu's configuration, about half a second before it shows the preview): the
|
|
253
|
+
// screen renders now if it hasn't yet, so that its content is there when the preview shows. Out of the command's
|
|
254
|
+
// runner, a failure rejects the push with its text and the preview goes. YES when the screen rendered. A preview of the
|
|
255
|
+
// JavaScript before a reload renders nothing: its screen would start in the new one.
|
|
256
|
+
+ (BOOL)renderAtPress:(ANNPreviewRecord *)record {
|
|
257
|
+
if (!record || record.done || !record.answer.isCurrent) {
|
|
258
|
+
return NO;
|
|
259
|
+
}
|
|
260
|
+
if (record.rendered) {
|
|
261
|
+
return YES;
|
|
262
|
+
}
|
|
263
|
+
@try {
|
|
264
|
+
[self parkAndRender:record];
|
|
265
|
+
} @catch (NSException *exception) {
|
|
266
|
+
[self finish:record];
|
|
267
|
+
[record.screen destroy];
|
|
268
|
+
[record.answer reject:[ANNCommandRunner rejectionOf:exception what:record.what]];
|
|
269
|
+
return NO;
|
|
270
|
+
}
|
|
271
|
+
return YES;
|
|
272
|
+
}
|
|
273
|
+
|
|
163
274
|
#pragma mark - The tap
|
|
164
275
|
|
|
165
276
|
// YES once the core took the preview: a key that leads back to an entry, or the push the queue accepted. From then on
|
|
@@ -206,7 +317,9 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
206
317
|
return YES;
|
|
207
318
|
}
|
|
208
319
|
// The preview grows into the screen: a push without an animation, and without a wait, as the screen rendered
|
|
209
|
-
// already; its answer at the end of the transaction.
|
|
320
|
+
// already; its answer at the end of the transaction. A screen that rendered only as the menu opened (a pointer's
|
|
321
|
+
// right click, then a quick tap) may have no content yet: its push waits for it as a push does.
|
|
322
|
+
BOOL waits = ANNPreviewRendersAtPress(screen) && !screen.reactView.isReady;
|
|
210
323
|
[stack setKey:key ofEntry:screen];
|
|
211
324
|
[stack.queue push:screen
|
|
212
325
|
answer:record.answer
|
|
@@ -214,7 +327,7 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
214
327
|
componentId:record.componentId
|
|
215
328
|
source:entry
|
|
216
329
|
animated:NO
|
|
217
|
-
waits:
|
|
330
|
+
waits:waits];
|
|
218
331
|
return YES;
|
|
219
332
|
}
|
|
220
333
|
|
|
@@ -231,9 +344,10 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
231
344
|
[record.answer reject:[ANNErrorTexts previewGone:record.previewId]];
|
|
232
345
|
}
|
|
233
346
|
|
|
234
|
-
// The record is done: the screen leaves its unseen place, the table forgets it.
|
|
347
|
+
// The record is done: the search stops, the screen leaves its unseen place, the table forgets it.
|
|
235
348
|
+ (void)finish:(ANNPreviewRecord *)record {
|
|
236
349
|
record.done = YES;
|
|
350
|
+
[self stopWatching:record];
|
|
237
351
|
[self unpark:record];
|
|
238
352
|
if (ANNPreviewRecords()[record.previewId] == record) {
|
|
239
353
|
[ANNPreviewRecords() removeObjectForKey:record.previewId];
|
|
@@ -286,6 +400,7 @@ static NSMutableDictionary<NSString *, ANNPreviewRecord *> *ANNPreviewRecords(vo
|
|
|
286
400
|
NSMutableArray<UIViewController *> *screens = [NSMutableArray new];
|
|
287
401
|
for (ANNPreviewRecord *record in ANNPreviewRecords().allValues) {
|
|
288
402
|
record.done = YES;
|
|
403
|
+
[self stopWatching:record];
|
|
289
404
|
[self unpark:record];
|
|
290
405
|
if (!record.attached) {
|
|
291
406
|
[screens addObject:record.screen];
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#import "ANNReactView.h"
|
|
3
3
|
#import <UIKit/UIKit.h>
|
|
4
4
|
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* The content of the tab bar's bottom accessory (iOS 26): the React component fills it, and the
|
|
7
9
|
* component gets `bottomAccessoryPlacementChanged` when UIKit moves the accessory between the
|
|
@@ -10,8 +12,17 @@
|
|
|
10
12
|
@interface ANNBottomTabsAccessoryView : UIView
|
|
11
13
|
|
|
12
14
|
- (instancetype)initWithReactView:(ANNReactView *)reactView
|
|
13
|
-
eventEmitter:(ANNEventEmitter *)eventEmitter;
|
|
15
|
+
eventEmitter:(nullable ANNEventEmitter *)eventEmitter;
|
|
14
16
|
|
|
15
17
|
@property(nonatomic, strong, readonly) ANNReactView *reactView;
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* `handler` runs once when the view is out of a window: at once when it is already, else when it
|
|
21
|
+
* leaves it, so a replaced accessory shows to the end of whatever UIKit draws of it. nil cancels a
|
|
22
|
+
* handler not run yet.
|
|
23
|
+
*/
|
|
24
|
+
- (void)whenOutOfWindow:(nullable dispatch_block_t)handler;
|
|
25
|
+
|
|
17
26
|
@end
|
|
27
|
+
|
|
28
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@implementation ANNBottomTabsAccessoryView {
|
|
4
4
|
ANNEventEmitter *_eventEmitter;
|
|
5
5
|
NSString *_placement;
|
|
6
|
+
dispatch_block_t _outOfWindowHandler;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
- (instancetype)initWithReactView:(ANNReactView *)reactView
|
|
@@ -32,6 +33,28 @@
|
|
|
32
33
|
}];
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
- (void)whenOutOfWindow:(dispatch_block_t)handler {
|
|
37
|
+
_outOfWindowHandler = nil;
|
|
38
|
+
if (!handler) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!self.window) {
|
|
42
|
+
handler();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
_outOfWindowHandler = [handler copy];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)didMoveToWindow {
|
|
49
|
+
[super didMoveToWindow];
|
|
50
|
+
dispatch_block_t handler = _outOfWindowHandler;
|
|
51
|
+
if (self.window || !handler) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
_outOfWindowHandler = nil;
|
|
55
|
+
handler();
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
// UIKit sets the environment when the accessory is shown and changes it as the tab bar minimizes
|
|
36
59
|
// and expands.
|
|
37
60
|
- (void)tabAccessoryEnvironmentDidChange API_AVAILABLE(ios(26.0)) {
|
|
@@ -598,10 +598,11 @@ static void ANNStacksOfNode(UIViewController *node, NSMutableArray<ANNStackContr
|
|
|
598
598
|
[[self tabsPresenter] tabsDidLayOut];
|
|
599
599
|
}
|
|
600
600
|
|
|
601
|
-
// `afterInitialTab` goes on at the tabs' first appearance at the latest.
|
|
601
|
+
// `afterInitialTab` goes on at the tabs' first appearance at the latest; a dot that waited for the window shows.
|
|
602
602
|
- (void)viewDidAppear:(BOOL)animated {
|
|
603
603
|
[super viewDidAppear:animated];
|
|
604
604
|
[self scheduleChain];
|
|
605
|
+
[[self tabsPresenter] tabsDidAppear];
|
|
605
606
|
}
|
|
606
607
|
|
|
607
608
|
#pragma mark - Rendering and the preload window
|
|
@@ -126,8 +126,13 @@ static const CGFloat ANNWideRowMargin = 16;
|
|
|
126
126
|
if (self.superview != host) {
|
|
127
127
|
[host addSubview:self];
|
|
128
128
|
}
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
if (host.subviews.lastObject != self) {
|
|
130
|
+
[host bringSubviewToFront:self];
|
|
131
|
+
}
|
|
132
|
+
// The system bar keeps its place and shows nothing; UIKit makes its subviews anew on a size change. Not the bar's
|
|
133
|
+
// `alpha`: UIKit puts it back to 1 when the bar shows, on a pop with `hidesBottomBarWhenPushed`, and on iOS 27 on
|
|
134
|
+
// a rotation and a theme change, after the tabs' layout (a check here misses it); alpha 0 also drops the system's
|
|
135
|
+
// soft fade at the bottom.
|
|
131
136
|
for (UIView *subview in bar.subviews) {
|
|
132
137
|
if (!subview.hidden) {
|
|
133
138
|
subview.hidden = YES;
|
|
@@ -193,6 +198,7 @@ static const CGFloat ANNWideRowMargin = 16;
|
|
|
193
198
|
extra = MAX(0, CGRectGetMinY(barFrame) - CGRectGetMinY(frame));
|
|
194
199
|
}
|
|
195
200
|
BOOL hidden = wide ? _hiddenByOptions : (tabs.barHidden || bar.hidden);
|
|
201
|
+
// A new size lays the cells out again by itself; the same frame changes nothing.
|
|
196
202
|
if (!CGRectEqualToRect(self.frame, frame)) {
|
|
197
203
|
self.frame = frame;
|
|
198
204
|
}
|
|
@@ -203,7 +209,6 @@ static const CGFloat ANNWideRowMargin = 16;
|
|
|
203
209
|
_background.layer.cornerRadius = radius;
|
|
204
210
|
}
|
|
205
211
|
[self setExtraSafeArea:hidden ? 0 : extra];
|
|
206
|
-
[self setNeedsLayout];
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
- (void)setHiddenByOptions:(BOOL)hiddenByOptions {
|
|
@@ -55,6 +55,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
55
55
|
/// The tabs laid out (and before they appear): the dots, the buttons' `testID`s, the custom row.
|
|
56
56
|
- (void)tabsDidLayOut;
|
|
57
57
|
|
|
58
|
+
/// The tabs appeared, in a window: a dot set while they were out of it (under a modal over the whole screen) is
|
|
59
|
+
/// placed now, as no layout may follow.
|
|
60
|
+
- (void)tabsDidAppear;
|
|
61
|
+
|
|
58
62
|
/// The bar laid out in a window for the first time: the titles are set again, so that the bar measures them at their
|
|
59
63
|
/// real place.
|
|
60
64
|
- (void)barDidLayOutInWindow;
|