@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +31 -0
- package/android/CLAUDE.md +38 -9
- package/android/CORE.md +11 -4
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +17 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +92 -19
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +79 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +29 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +45 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +72 -16
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +164 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +107 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +131 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
- package/cli/doctor/checks/app.js +61 -13
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/docs-md/architecture/transitions.md +12 -7
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +4 -2
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +20 -3
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +8 -2
- package/docs-md/screen-ui/system-ui.md +1 -1
- package/docs-md/start/installation.md +3 -1
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +13 -8
- package/ios/Contract/ANNCommandAnswer.mm +5 -20
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.mm +1 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -3
- package/ios/Core/ANNNavigationState.mm +10 -5
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +44 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +5 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +15 -7
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +18 -13
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +6 -3
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +56 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +15 -7
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/modals.ts +18 -13
- package/src/interfaces/options/system.ts +6 -3
- package/src/interfaces/options/topBar.ts +22 -9
|
@@ -46,6 +46,8 @@ navigation.showModal('Player', {
|
|
|
46
46
|
});
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
The top bar of a pushed screen goes with the screen on Android and stays in place on iOS: see [the bar in a transition of your own](#custom-bar).
|
|
50
|
+
|
|
49
51
|
`presentation` of `createNavigation` gives every push or modal of the app its transition: see [Defaults for the whole app](../fundamentals/moving-between-screens#presentation). New roots have presets of their own: see [How a new root comes in](../fundamentals/switching-roots#transitions).
|
|
50
52
|
|
|
51
53
|
## Transitions out of a view
|
|
@@ -144,7 +146,11 @@ navigation.push('Details', {
|
|
|
144
146
|
|
|
145
147
|
Details rises 300 points as it fades in. Each property animates from a value to a value over a time, with a delay and an interpolation if it needs them; the [API](#api) lists every one.
|
|
146
148
|
|
|
147
|
-
The same goes for `pop`, `showModal`, `dismissModal`, `setStackRoot` and `setRoot`. The swipe back is the exception: (iOS) it always follows the finger with the system's slide, so a custom `pop` plays for the back button and `pop()` only.
|
|
149
|
+
The same goes for `pop`, `showModal`, `dismissModal`, `setStackRoot` and `setRoot`. The swipe back is the exception: (iOS) it always follows the finger with the system's slide, so a custom `pop` plays for the back button and `pop()` only. (Android) A pop with an animation of its own shows no preview under the swipe: the screen pops when the swipe ends.
|
|
150
|
+
|
|
151
|
+
### The top bar
|
|
152
|
+
|
|
153
|
+
The top bar goes its platform's way in a preset or an animation of your own. (Android) It goes with its screen, as if it were part of it: with `slideUp` the new screen comes up with its bar over the old one and its bar, and on the way back it goes down with its bar, uncovering the bar below. With `fade` or `scale` the bar fades or grows with its screen. (iOS) UIKit keeps the bar in place and changes its title and buttons as the transition starts; only the screens under it move.
|
|
148
154
|
|
|
149
155
|
## Which to pick
|
|
150
156
|
|
|
@@ -162,11 +168,10 @@ A screen can come in before its React content has drawn, or wait for it. Coming
|
|
|
162
168
|
|
|
163
169
|
| | By default |
|
|
164
170
|
| --- | --- |
|
|
165
|
-
|
|
|
166
|
-
| (Android) push | Doesn't wait. With `appProvider` a React screen is rendered before the push starts, so it comes in drawn from the first frame. A screen with a React root of its own comes in at once, and its content draws as it comes; so does a native screen. The components of the bar draw as the screen comes in. |
|
|
171
|
+
| Push | Waits for the screen's first mount, half a second at most, so it comes in drawn, with what it set as it rendered (a title from `useScreenOptions`) already in its bar. A native screen comes in at once. (iOS) The components of the bar wait too, each to its own limit from the start of the wait. (Android) The components of the bar draw as the screen comes in; with `appProvider` the screen is rendered before the push starts ([below](#wait-one-tree)). |
|
|
167
172
|
| (Android) modal | Waits for the content and the components of the bar, half a second at most, so the screen comes in drawn. |
|
|
168
173
|
|
|
169
|
-
`waitForRender` of an animation changes it. `true` waits as long as it takes; `false` comes in at once (a push on Android with `appProvider` [still renders the screen first](#wait-one-tree)):
|
|
174
|
+
`waitForRender` of an animation changes it. `true` waits as long as it takes; `false` comes in at once, the content drawing as it comes (a push on Android with `appProvider` [still renders the screen first](#wait-one-tree)):
|
|
170
175
|
|
|
171
176
|
```ts
|
|
172
177
|
navigation.push('Details', {
|
|
@@ -174,15 +179,15 @@ navigation.push('Details', {
|
|
|
174
179
|
});
|
|
175
180
|
```
|
|
176
181
|
|
|
177
|
-
A screen that draws nothing at first
|
|
182
|
+
A screen that draws nothing at first holds its transition back half a second at most, then comes in with its bar.
|
|
178
183
|
|
|
179
184
|
### A push on Android, in one tree
|
|
180
185
|
|
|
181
|
-
(Android) With `appProvider`
|
|
186
|
+
(Android) With `appProvider` the screen comes in drawn without a wait of its own: nothing of it mounts during the transition, which would drop frames. It goes in three steps:
|
|
182
187
|
|
|
183
188
|
1. The native side lays the screen out in its stack, out of sight, and gives React its size and its safe area.
|
|
184
189
|
2. React renders the screen at that size with its content, at once.
|
|
185
|
-
3. The push starts
|
|
190
|
+
3. The push starts right after that content appears, once what the screen set as it rendered (its `useScreenOptions`) is applied; if it doesn't appear, after half a second.
|
|
186
191
|
|
|
187
192
|
So the push starts later by the time React takes to render the screen, as a push in a native app starts once its screen is built. `waitForRender: false` goes the same way here; `true` waits for the content as long as it takes, and for the components of the bar, half a second at most.
|
|
188
193
|
|
|
@@ -70,6 +70,8 @@ navigation.showModal('Compose', { style: 'fullScreen' });
|
|
|
70
70
|
|
|
71
71
|
Without `style`, a modal is a sheet on (iOS) and covers the whole screen on (Android).
|
|
72
72
|
|
|
73
|
+
`'pageSheet'` and `'formSheet'` are sheets on both platforms: on (Android) the Material bottom sheet over the dimmed screen, at the full height under the status bar and at most 640 dp wide. `'fullScreen'` and `'overFullScreen'` come up from the bottom over the whole screen; on Android with the system's motion of a window opening over another, without a fade.
|
|
74
|
+
|
|
73
75
|
`'overFullScreen'` keeps the screen under the modal in place, so it shows through wherever the modal draws nothing: a dialog of your own, a card over a dimmed app. Give the modal's screen `backgroundColor('transparent')` in its options, so its own background doesn't hide the screen under it.
|
|
74
76
|
|
|
75
77
|
### A form on iPad
|
|
@@ -83,7 +85,7 @@ navigation.showModal('EditProfile', {
|
|
|
83
85
|
});
|
|
84
86
|
```
|
|
85
87
|
|
|
86
|
-
On iPhone a form sheet is a sheet, and `contentSize` does nothing.
|
|
88
|
+
On iPhone and on Android a form sheet is a sheet, and `contentSize` does nothing.
|
|
87
89
|
|
|
88
90
|
### A popover
|
|
89
91
|
|
|
@@ -95,7 +97,7 @@ navigation.showModal('Filters', {
|
|
|
95
97
|
});
|
|
96
98
|
```
|
|
97
99
|
|
|
98
|
-
On iPhone it becomes a sheet, unless `adaptive: false` keeps it a popover. (Android) has no popovers: the modal
|
|
100
|
+
On iPhone it becomes a sheet, unless `adaptive: false` keeps it a popover. (Android) has no popovers: the modal is the bottom sheet of `'pageSheet'`.
|
|
99
101
|
|
|
100
102
|
## Sheets
|
|
101
103
|
|
|
@@ -24,7 +24,9 @@ This page answers:
|
|
|
24
24
|
|
|
25
25
|
A push or a modal moves the user somewhere. An overlay doesn't move anyone: it puts a screen **on top of everything**, and the app goes on under it.
|
|
26
26
|
|
|
27
|
-
An overlay is a screen of `screens` like any other: a component, its props, its options. It shows above the tabs, the bars and the modals; on (iOS) it is in a window of its own. Its background is transparent, so the app shows wherever the overlay draws nothing.
|
|
27
|
+
An overlay is a screen of `screens` like any other: a component, its props, its options. It shows above the tabs, the bars and the modals; on (iOS) it is in a window of its own. Its background is transparent, so the app shows wherever the overlay draws nothing.
|
|
28
|
+
|
|
29
|
+
An overlay covers the whole window, **the status bar included**: a dialog's dimmed backdrop dims the status bar too, as a system dialog does. Its safe area keeps the content off the bars, so a toast at the top of the overlay sits below the status bar. On (Android) a status bar with a color of its own (`statusBar.backgroundColor`) stays over the backdrop, and `statusBar.drawBehind: false` starts the overlay below the status bar. What overlays are for:
|
|
28
30
|
|
|
29
31
|
## Showing and closing
|
|
30
32
|
|
|
@@ -74,7 +74,7 @@ A tap on the button opens the menu. A button of the options gets the navigation
|
|
|
74
74
|
|
|
75
75
|
## A pick in the menu
|
|
76
76
|
|
|
77
|
-
A menu is a screen without a stack, so **its commands act on the center**, and the menu closes
|
|
77
|
+
A menu is a screen without a stack, so **its commands act on the center**, and the menu closes as the center changes: it starts to slide away at the tap, and a push slides in as it goes. The command resolves once both are done. A pick usually switches the section, so the center starts over with it:
|
|
78
78
|
|
|
79
79
|
```tsx
|
|
80
80
|
import { Button, View } from 'react-native';
|
|
@@ -94,7 +94,7 @@ function Menu() {
|
|
|
94
94
|
}
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
A tap on Settings shows Settings in the center
|
|
97
|
+
A tap on Settings closes the menu and shows Settings in the center as it goes. Which command to use depends on the way back:
|
|
98
98
|
|
|
99
99
|
| From the menu | The center |
|
|
100
100
|
| --- | --- |
|
|
@@ -108,6 +108,8 @@ A tap on Settings shows Settings in the center, and the menu closes. Which comma
|
|
|
108
108
|
| (iOS) | Over the content, which dims: `openMode: 'aboveContent'` of the side, the default. Or under it, with the content moved aside: `'pushContent'`. Then `animation(…)` says how the menu appears: `'parallax'`, `'slide'`, `'slide-and-scale'` or `'door'`. |
|
|
109
109
|
| (Android) | The drawer of the platform: over the content, which dims. |
|
|
110
110
|
|
|
111
|
+
The menu's color is its screen's `layout.backgroundColor`: it fills the whole height of the menu, behind the status bar and the navigation bar, while the menu's content keeps clear of them. A background on the React content stops at the safe area.
|
|
112
|
+
|
|
111
113
|
`.ios()` keeps the settings that only iOS has apart from the others:
|
|
112
114
|
|
|
113
115
|
```ts
|
|
@@ -90,6 +90,8 @@ This is the app's `navigation`: its `push` goes to the stack the user sees, now
|
|
|
90
90
|
|
|
91
91
|
Inbox shows, and Chat comes in over it, in the stack of the Inbox tab.
|
|
92
92
|
|
|
93
|
+
On (Android) the content changes with Material's fade through, as in the system's Phone app: the tab that goes fades out, then the new one fades in and grows to its size, in 300 ms; the tab bar stays. On (iOS) the new tab shows at once, as the system's tab bar does.
|
|
94
|
+
|
|
93
95
|
### A press on a tab
|
|
94
96
|
|
|
95
97
|
A second tap on the selected tab is a convention of both platforms: **back to the start of the tab**. The navigation does it without code:
|
|
@@ -299,7 +299,7 @@ navigation.showModal('EditProfile', {
|
|
|
299
299
|
});
|
|
300
300
|
```
|
|
301
301
|
|
|
302
|
-
On iPhone a form sheet is the same sheet as before
|
|
302
|
+
On iPhone a form sheet is the same sheet as before. On Android it is the Material bottom sheet, where the modal without a style covered the whole screen.
|
|
303
303
|
|
|
304
304
|
## The whole code
|
|
305
305
|
|
package/docs-md/help/glossary.md
CHANGED
|
@@ -266,7 +266,7 @@ Two screens shown as one entry of a stack: the primary, whose bar shows, and the
|
|
|
266
266
|
|
|
267
267
|
### Popover
|
|
268
268
|
|
|
269
|
-
(iPadOS) A modal that points at the bar button or the view that opened it. On iPhone
|
|
269
|
+
(iPadOS) A modal that points at the bar button or the view that opened it. On iPhone and on Android it becomes a sheet. More: [A popover](../containers/modals#popover).
|
|
270
270
|
|
|
271
271
|
### Presentation
|
|
272
272
|
|
|
@@ -370,7 +370,7 @@ The system bar at the top of the screen, with the time and the battery. Each scr
|
|
|
370
370
|
|
|
371
371
|
### Swipe back
|
|
372
372
|
|
|
373
|
-
(iOS) The swipe from the left edge, and on iOS 26 from the content, that goes back following the finger. `popGesture` narrows it or turns it off. More: [The swipe back](../screen-ui/back-and-gestures#swipe).
|
|
373
|
+
(iOS) The swipe from the left edge, and on iOS 26 from the content, that goes back following the finger. `popGesture` narrows it or turns it off. (Android) 14+ The swipe of the system back shows the pop under the finger, and the user can stop halfway. More: [The swipe back](../screen-ui/back-and-gestures#swipe).
|
|
374
374
|
|
|
375
375
|
### System back
|
|
376
376
|
|
|
@@ -73,13 +73,30 @@ Options set in `static options` on a component no longer apply.
|
|
|
73
73
|
|
|
74
74
|
**Fix:** move them into `defineScreen({ component, options })`; the object carries over as it is. More: [Options next to the screen](../migration/from-react-native-navigation#options).
|
|
75
75
|
|
|
76
|
+
### The Android app crashes at start: React Native isn't loaded
|
|
77
|
+
|
|
78
|
+
(Android) The app closes at once with `NavigationApplication needs React Native loaded first`, and `doctor` reports `MainApplication.kt doesn't call loadReactNative(this) in onCreate`.
|
|
79
|
+
|
|
80
|
+
**Why:** `NavigationApplication` leaves the load of React Native to the template's `loadReactNative(this)`, which loads it as the app's Gradle files say (the New Architecture, edge-to-edge). Without the call nothing loads it.
|
|
81
|
+
|
|
82
|
+
**Fix:** keep the template's `onCreate`, or run `npx apex-navigation setup`, which adds it:
|
|
83
|
+
|
|
84
|
+
```kotlin
|
|
85
|
+
override fun onCreate() {
|
|
86
|
+
super.onCreate()
|
|
87
|
+
loadReactNative(this)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
More: [Android files](../start/installation#android-files).
|
|
92
|
+
|
|
76
93
|
### The Android app crashes at start: React Native loads twice
|
|
77
94
|
|
|
78
|
-
(Android) The app closes at once, and `doctor` reports `MainApplication.kt
|
|
95
|
+
(Android) The app closes at once, and `doctor` reports `MainApplication.kt loads React Native itself … next to loadReactNative(this)`.
|
|
79
96
|
|
|
80
|
-
**Why:**
|
|
97
|
+
**Why:** the application loads React Native on its own as well, with `SoLoader.init` or the New Architecture's entry point (`load()`, as apps from react-native-navigation have it), and a second load crashes the app.
|
|
81
98
|
|
|
82
|
-
**Fix:**
|
|
99
|
+
**Fix:** keep only `loadReactNative(this)` in `onCreate`; `npx apex-navigation setup` removes the other load. More: [Android files](../start/installation#android-files).
|
|
83
100
|
|
|
84
101
|
### doctor warns about the JDK
|
|
85
102
|
|
package/docs-md/index.json
CHANGED
|
@@ -974,6 +974,10 @@
|
|
|
974
974
|
"heading": "The system back of Android",
|
|
975
975
|
"anchor": "system-back"
|
|
976
976
|
},
|
|
977
|
+
{
|
|
978
|
+
"heading": "The preview of a pop",
|
|
979
|
+
"anchor": "pop-preview"
|
|
980
|
+
},
|
|
977
981
|
{
|
|
978
982
|
"heading": "Back to the home screen",
|
|
979
983
|
"anchor": "system-animation"
|
|
@@ -1323,6 +1327,10 @@
|
|
|
1323
1327
|
"heading": "Animations of your own",
|
|
1324
1328
|
"anchor": "custom"
|
|
1325
1329
|
},
|
|
1330
|
+
{
|
|
1331
|
+
"heading": "The top bar",
|
|
1332
|
+
"anchor": "custom-bar"
|
|
1333
|
+
},
|
|
1326
1334
|
{
|
|
1327
1335
|
"heading": "Which to pick",
|
|
1328
1336
|
"anchor": "which"
|
|
@@ -3944,6 +3952,10 @@
|
|
|
3944
3952
|
"heading": "A screen ignores its static options",
|
|
3945
3953
|
"anchor": "static-options"
|
|
3946
3954
|
},
|
|
3955
|
+
{
|
|
3956
|
+
"heading": "The Android app crashes at start: React Native isn't loaded",
|
|
3957
|
+
"anchor": "not-loaded"
|
|
3958
|
+
},
|
|
3947
3959
|
{
|
|
3948
3960
|
"heading": "The Android app crashes at start: React Native loads twice",
|
|
3949
3961
|
"anchor": "loads-twice"
|
|
@@ -140,7 +140,7 @@ Each piece of `react-native-navigation`, and what takes its place. The [steps](#
|
|
|
140
140
|
| `AppRegistry` left out of `index.js` | `AppRegistry.registerComponent(name, Navigation)` of the package. |
|
|
141
141
|
| `RNNAppDelegate`, `import ReactNativeNavigation` | `ANNAppDelegate`, `import ApexNavigation`. |
|
|
142
142
|
| `com.reactnativenavigation.*` | `dev.apexnative.navigation.*`. |
|
|
143
|
-
| `NavigationReactNativeHost` in `MainApplication` | The template's `reactHost
|
|
143
|
+
| `NavigationReactNativeHost` in `MainApplication` | The template's `reactHost`, and its `loadReactNative(this)` in `onCreate`. |
|
|
144
144
|
| `RNNKotlinVersion` in `android/build.gradle` | Nothing: the library reads `kotlinVersion`. |
|
|
145
145
|
|
|
146
146
|
## The move, step by step
|
|
@@ -212,11 +212,12 @@ import dev.apexnative.navigation.NavigationActivity
|
|
|
212
212
|
class MainActivity : NavigationActivity()
|
|
213
213
|
```
|
|
214
214
|
|
|
215
|
-
The application drops `NavigationReactNativeHost` and keeps the `reactHost` of the React Native template.
|
|
215
|
+
The application drops `NavigationReactNativeHost` and keeps the `reactHost` of the React Native template. react-native-navigation loaded React Native itself; the navigation leaves it to the template's `loadReactNative(this)` in `onCreate`, which `migrate` adds.
|
|
216
216
|
|
|
217
217
|
```kotlin
|
|
218
218
|
import com.facebook.react.PackageList
|
|
219
219
|
import com.facebook.react.ReactHost
|
|
220
|
+
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
|
|
220
221
|
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
|
221
222
|
import dev.apexnative.navigation.NavigationApplication
|
|
222
223
|
|
|
@@ -227,6 +228,11 @@ class MainApplication : NavigationApplication() {
|
|
|
227
228
|
packageList = PackageList(this).packages,
|
|
228
229
|
)
|
|
229
230
|
}
|
|
231
|
+
|
|
232
|
+
override fun onCreate() {
|
|
233
|
+
super.onCreate()
|
|
234
|
+
loadReactNative(this)
|
|
235
|
+
}
|
|
230
236
|
}
|
|
231
237
|
```
|
|
232
238
|
|
|
@@ -17,7 +17,7 @@ On iOS the back button of Details reads "Inbox". This page answers:
|
|
|
17
17
|
|
|
18
18
|
1. [The ways back](#ways) on each platform.
|
|
19
19
|
2. [The back button](#back-button): [its title](#title) and [its look](#look).
|
|
20
|
-
3. [The swipe back](#swipe) of
|
|
20
|
+
3. [The swipe back](#swipe) of both platforms, and [the system back](#system-back) of Android.
|
|
21
21
|
4. [How to keep the user on a screen](#prevent) with unsaved changes.
|
|
22
22
|
|
|
23
23
|
## The ways back
|
|
@@ -85,7 +85,7 @@ export const MapScreen = defineScreen({
|
|
|
85
85
|
| `'edge'` | From the edge only: a swipe on the content stays with the content. |
|
|
86
86
|
| `false` | Off. The back button still works. |
|
|
87
87
|
|
|
88
|
-
(Android) The system back
|
|
88
|
+
(Android) The swipe is the system's back, from either edge, and `popGesture` doesn't apply to it. On Android 14+ it shows the pop before the user lets go: both screens shrink, the screen under comes in behind a dim, and the screen on top follows the finger. The user can stop halfway and stay, as on iOS. On Android 13 and older the screen pops when the swipe ends. When the preview shows and when it doesn't: [the preview of a pop](#pop-preview).
|
|
89
89
|
|
|
90
90
|
## The system back of Android
|
|
91
91
|
|
|
@@ -100,6 +100,12 @@ export const MapScreen = defineScreen({
|
|
|
100
100
|
| The first screen of a tab | What `Tabs().backBehavior(…)` says: leave the app, go to the first tab, or to the tab before. See [Back on Android](../containers/tabs#back). |
|
|
101
101
|
| The first screen of the app | Leaves the app, as every Android app does, with the system's own animation ([below](#system-animation)). `usePreventLeave` doesn't hold it; to ask first, see [Asking before the user leaves the app](/api/back-and-gestures/usePreventLeave#leaving-the-app). |
|
|
102
102
|
|
|
103
|
+
### The preview of a pop
|
|
104
|
+
|
|
105
|
+
(Android) 14+ The back gesture on a pushed screen shows where it goes before the user lets go, as the system does between apps' screens: the screen shrinks toward the side of the swipe with rounded corners and follows the finger, and the screen under it shows behind it, dimmed, with its own bar. Letting go pops the screen; swiping back cancels, and the screen grows back into place.
|
|
106
|
+
|
|
107
|
+
The preview shows when back pops the screen with the default transition. It doesn't when back does something else first, or when the app takes it: a menu or a search that closes, a screen that holds back (`hardwareBackButton.popStackOnPress: false`, a [`BackHandler` listener](#back-handler), `usePreventLeave`), or a pop with an animation of its own (`animations.pop`). On Android 13 and older the screen pops when the swipe ends.
|
|
108
|
+
|
|
103
109
|
### Back to the home screen
|
|
104
110
|
|
|
105
111
|
(Android) On the first screen of the app, with nothing left to go back to, the navigation leaves the back to the system. The system then plays its predictive back animation: as the user swipes, the app shrinks and the home screen shows behind it, and the user can still change their mind halfway. The system decides this as the swipe starts, so the navigation takes the back only while it has something to do with it: a screen to pop, a modal, a menu or a search to close, a tab to go to (`backBehavior`), or a [`BackHandler` listener](#back-handler) of the app.
|
|
@@ -249,7 +249,7 @@ iPhone Duo decides by itself too: folded, the outer display shows the app in por
|
|
|
249
249
|
|
|
250
250
|
## Backgrounds
|
|
251
251
|
|
|
252
|
-
`backgroundColor` colors the screen, behind its React content. Without it, the screen has the background of the [theme](../architecture/theme-and-context).
|
|
252
|
+
`backgroundColor` colors the screen, behind its React content. Without it, the screen has the background of the [theme](../architecture/theme-and-context), or the system's when the theme has none: `systemBackground` on (iOS), `android:colorBackground` of the app's theme on (Android) (a screen in a sheet shows the sheet's surface; a native screen paints its own view). So a React screen is never see-through by accident; `'transparent'` makes it so on purpose, as overlays are.
|
|
253
253
|
|
|
254
254
|
```ts
|
|
255
255
|
s.backgroundColor('#F3E9D2');
|
|
@@ -126,11 +126,13 @@ In an app as the React Native template makes it, the window shows **one React vi
|
|
|
126
126
|
|
|
127
127
|
| File | What setup does | Why |
|
|
128
128
|
| --- | --- | --- |
|
|
129
|
-
| `MainApplication.kt` | Extends `NavigationApplication
|
|
129
|
+
| `MainApplication.kt` | Extends `NavigationApplication`; keeps the template's `loadReactNative(this)` in `onCreate`, or adds it. | It keeps one React host for all screens, made after `loadReactNative(this)` loaded React Native. Native screens made by `codegen` are registered there, before JS opens them. |
|
|
130
130
|
| `MainActivity.kt` | Extends `NavigationActivity`. | The activity holds the containers of the navigation instead of one React view. It gets the back press, edge-to-edge and configuration changes first. |
|
|
131
131
|
| `AndroidManifest.xml` | Adds `android:enableOnBackInvokedCallback="true"` to `<application>`. | On the first screen the system plays its own animation back to the home screen as the user swipes back (predictive back). Android 16 turns it on for apps that target API 36; an app that targets less opts in here. A value the app set itself stays. See [Back to the home screen](../screen-ui/back-and-gestures#system-animation). |
|
|
132
132
|
| `styles.xml` | `AppTheme` inherits `Theme.ApexNavigation`. | The bars, tabs and search are Material 3 views and take their colors from the app's theme. This one is Material 3 in neutral grays, light and dark; change any color in the same file. A theme of the app's own stays as it is. See [Android's colors](../architecture/theme-and-context#android). |
|
|
133
133
|
|
|
134
|
+
The main activity keeps the template's `android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"`. Without `orientation`, `screenSize`, `smallestScreenSize`, `screenLayout` or `uiMode` Android recreates the activity on a rotation, a resize, a fold or a light/dark switch, and every screen goes with it; `doctor` warns about it under Config changes.
|
|
135
|
+
|
|
134
136
|
#### iOS files
|
|
135
137
|
|
|
136
138
|
| File | What setup does | Why |
|
package/ios/CLAUDE.md
CHANGED
|
@@ -27,7 +27,7 @@ Swift is not used here: apps reach the core through the public headers (`Public/
|
|
|
27
27
|
- `Layouts/SplitView/`: The split view module, plus the core's `ANNSplitViewPresenter` and `ANNSplitViewCommands`.
|
|
28
28
|
- `Layouts/Arrangement/`: The pair (iOS 27.1): `ANNArrangementViewController`, `ANNArrangementState`.
|
|
29
29
|
- `TopBar/`: The bar of a screen: title, buttons, back button, large title, appearance, search, toolbar, edge effects.
|
|
30
|
-
- `Options/`, `Options/Params`: One `ANN<Group>Options` class per group, the field table, the value kinds and parsers (`Bool`, `Color`, `Text`, `Image`, …).
|
|
30
|
+
- `Options/`, `Options/Params`: One `ANN<Group>Options` class per group, the field table, the value kinds and parsers (`Bool`, `Color`, `Text`, `Image`, …), the images' cache and loader (`ANNOptionImages`).
|
|
31
31
|
- `Animations/`: The custom transition engine: plan, animator, wait, frame driver, rules; `Elements/` and `Interpolators/`.
|
|
32
32
|
- `React/`, `React/Slots/`: React views of screens and components, the surface host, the safe area view, the slot host and slot screens.
|
|
33
33
|
- `Public/ApexNavigation/`: The headers apps import: `ApexNavigation.h`, `ANNAppDelegate.h`, `ANNSceneDelegate.h`.
|
|
@@ -42,7 +42,7 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
42
42
|
- Launch, the app-facing API — `Public/ApexNavigation/`; `Core/`: `ApexNavigation.mm`, `ANNAppDelegate.mm`, `ANNSceneDelegate.mm`, `ApexNavigation+Internal.h`
|
|
43
43
|
- Root, splash, reload — `Layouts/Root/`: `ANNRootCoordinator`, `ANNSplashViewController`; `Core/`: `ANNReloadCoordinator`
|
|
44
44
|
- Screen: React root, slot, native screen — `Layouts/Screen/`: `ANNComponentViewController`; `React/`: `ANNReactView`, `ANNReactViewCreator`, `Slots/`; `Layouts/External/`
|
|
45
|
-
- Screen: insets, background, keyboard focus — `Layouts/Screen/`: `ANNScreenInsets`, `ANNScreenSafeArea`, `ANNScreenBars`, `ANNComponentPresenter` (background, status bar blur), `ANNComponentViewController` (key commands)
|
|
45
|
+
- Screen: insets, background, keyboard focus — `Layouts/Screen/`: `ANNScreenInsets`, `ANNScreenSafeArea`, `ANNScreenBars`, `ANNScreenPlace` (the safe area of a parked screen), `ANNComponentPresenter` (background, status bar blur), `ANNComponentViewController` (key commands)
|
|
46
46
|
- Safe area view — `React/`: `ANNSafeAreaViewComponentView`; `Layouts/Screen/`: `ANNScreenReadiness`; `cpp/`
|
|
47
47
|
- Stack: queue, commands, back, gestures — `Layouts/Stack/`: `ANNStackQueue`, `ANNStackCommands`, `ANNStackController`, `InteractivePopGestureDelegate`, `ANNStackAnimatingDelegate`
|
|
48
48
|
- Top bar: title, buttons, menus, large title — `TopBar/`: `ANNTopBarPresenter`, `ANNBarButtons`, `ANNBarAppearance`, `ANNLargeTitlePresenter`, `ANNBackButtonPresenter`, `ANNTitleView`, `ANNTitleGlassView`
|
|
@@ -60,24 +60,31 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
60
60
|
- System bars, orientation, direction — `Core/`: `ANNSystemUI`, `ANNSystemUIRules`, `ANNAppDirection`, `ANNNodePresenter` (the answers to UIKit)
|
|
61
61
|
- Events and state — `Core/`: `ANNEventEmitter`, `ANNEventContract` (names and keys, made by `scripts/contract.mjs`), `ANNAppearanceDispatcher`, `ANNScreenAppearance`, `ANNNavigationState`; `TurboModules/`: `ANNTurboEventEmitter`
|
|
62
62
|
- Options: parsing, merging, defaults — `Options/`: `ANNOptions`, `ANNOptionField`, `ANNOptionsReader`, `ANNDefaultOptions`, `ANNOptionsCache`; `Core/`: `ANNOptionsRepaint`
|
|
63
|
-
- Error texts — `Contract/`: `ANNErrorTexts` (made from `src/interfaces/contract/errors.json` by `scripts/contract.mjs`); `Core/`: `ANNErrorHandler`
|
|
63
|
+
- Error texts and answers — `Contract/`: `ANNErrorTexts` and `ANNCommandContract` (made from `src/interfaces/contract/errors.json` and `commands.json` by `scripts/contract.mjs`); `Core/`: `ANNErrorHandler`
|
|
64
64
|
- Links — `Core/`: `ANNDeepLinkQueue`; the `getLaunchLink` method of `ANNTurboModule`
|
|
65
65
|
- Key commands — `Core/`: `ANNKeyCommands`; `Layouts/Screen/`: `ANNComponentViewController`
|
|
66
66
|
- Hinge, regions — `Core/`: `ANNHinge`; `Layouts/Screen/`: `ANNScreenRegions`; `Layouts/Arrangement/`: `ANNArrangementState`
|
|
67
67
|
- Native screens — `Layouts/External/`: `ANNExternalViewController`, `ANNNativeScreenCalls`; `Layouts/Screen/ANNNativeScreenContext.mm`
|
|
68
|
-
- Slots — `React/Slots/`: `ANNSlotHost`, `ANNSlotContainers
|
|
68
|
+
- Slots — `React/Slots/`: `ANNSlotHost`, `ANNSlotContainers` (containers and slot views by slot id: a change goes
|
|
69
|
+
straight to the views of its id), `ANNSlotScreenView`, `ANNSlotComponentView`; `cpp/`
|
|
69
70
|
|
|
70
71
|
## Threads
|
|
71
72
|
|
|
72
73
|
- **Module queue:** the layout check (`ANNLayoutReader`) and the sync getters. The check reads only stores safe from
|
|
73
74
|
any thread (`ApexNavigation hasNativeScreenNamed:`, `ANNArrangementState canShowArrangements`).
|
|
74
75
|
- **Main queue:** everything else. A command body starts with `ANNAssertMainQueue()`. The options of a layout are parsed
|
|
75
|
-
here (`ANNLayoutTreeOptions
|
|
76
|
+
here (`ANNLayoutTreeOptions`; an image keeps only its source). `ANNEventEmitter` moves an event from another queue
|
|
76
77
|
to the main queue.
|
|
78
|
+
- **The images' queue** (`ANNOptionImages`, `Options/Params`): every option image decodes there, through one cache.
|
|
79
|
+
An element that shows an image asks for it when it applies and waits for it (the first frame needs it); only a tab
|
|
80
|
+
item's new icon on the bar that shows comes later (`ANNBottomTabsPresenter`), the last build of the item winning.
|
|
77
81
|
- **Sync getters** answer JS during a screen's first render: `getScreenInsets`, `getScreenRegions`,
|
|
78
82
|
`getArrangementState`, `getHinge`, `getStartInfo`. They read `ANNScreenInsets`, `ANNScreenRegions`,
|
|
79
83
|
`ANNArrangementState`, `ANNHinge lastValue`, written on the main queue and guarded by `@synchronized`.
|
|
80
84
|
`ANNReloadCoordinator.generation` and `ANNEventEmitter.isCurrent` are readable from any thread.
|
|
85
|
+
- **After the calls of JavaScript:** `ANNJavaScriptCalls afterCallsMadeSoFar:` runs a step on the main queue after
|
|
86
|
+
every call JavaScript made by then, by the calls' own way (the JavaScript thread, the module's queue, the main
|
|
87
|
+
queue). A push's wait ends through it, so the `mergeOptions` of its screen's first commit come first.
|
|
81
88
|
|
|
82
89
|
## Checks
|
|
83
90
|
|
|
@@ -92,6 +99,11 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
92
99
|
|
|
93
100
|
- Native code never touches React Native's views (their subviews, their layout, their frames): Fabric owns and recycles
|
|
94
101
|
them. Read them; what a transition writes (`alpha`, `transform`, a hidden flag) goes back on every path.
|
|
102
|
+
- A screen parked by a wait for its content (`ANNParking`) has no parent, and UIKit gives its view no safe area: it lays
|
|
103
|
+
out with the safe area of its place (`ANNScreenPlace`, measured on a navigation controller of the core's), or its
|
|
104
|
+
list would move as the push starts and UIKit would show the large title only after the transition. On iOS 26 its
|
|
105
|
+
large title also keeps a place of the core's for the push (`arrivesByPushOn:`). Check Showcase 1.4 (with a font
|
|
106
|
+
size), every section panel and 12.6 on push, pop and swipe after any change to waits, parking or the large title.
|
|
95
107
|
- Every change of a stack's children goes through its `ANNStackQueue`. The core's own calls to UIKit's navigation
|
|
96
108
|
come only from there; UIKit's own pops are followed (`uikitShows:`), not fought.
|
|
97
109
|
- Every UIKit present and dismiss in the main window is a step in `ANNPresentationGate`. UIKit drops a transition
|
package/ios/CORE.md
CHANGED
|
@@ -68,16 +68,20 @@ dispatcher.
|
|
|
68
68
|
`+[ANNErrorTexts nowhereToShow]` when there is no window, then runs the body in `@try`. The body finds Home in
|
|
69
69
|
`ANNNodeRegistry` and calls `+[ANNStackCommands push:componentId:what:answer:]`.
|
|
70
70
|
4. **`ANNStackCommands`.** Finds the stack by walking `layoutParent`; checks `ANNLayoutValidator` (an id already shown,
|
|
71
|
-
what a stack takes); reads the options of the whole tree with `ANNLayoutTreeOptions` (
|
|
72
|
-
|
|
73
|
-
goes back to that entry; else
|
|
71
|
+
what a stack takes); reads the options of the whole tree with `ANNLayoutTreeOptions` (an image keeps its source,
|
|
72
|
+
loaded through `ANNOptionImages` when an element shows it); checks `ANNLayoutFactory canMakeTrees:`; a node with
|
|
73
|
+
`preview.nativeID` goes to `ANNPreviewPush`; a `key` of an entry below goes back to that entry; else
|
|
74
|
+
`+[ANNLayoutFactory nodeFromLayout:options:]` makes Profile (an
|
|
74
75
|
`ANNComponentViewController` with an `ANNComponentPresenter`, content from `ANNReactViewCreator`) and registers it.
|
|
75
76
|
No view is rendered yet.
|
|
76
77
|
5. **`-[ANNStackQueue push:answer:…]`.** The node joins `entries` now (every check reads `entries`), the operation is
|
|
77
78
|
queued, `runNext` starts it when the queue is at rest.
|
|
78
79
|
6. **`startPush:` and `startPushTransition:`.** By `animations.push.waitForRender` the queue waits through
|
|
79
|
-
`ANNRenderWait
|
|
80
|
-
|
|
80
|
+
`ANNRenderWait`: unset, for the content at most 0.5 s, with `true` as long as it takes, then for the bar components
|
|
81
|
+
(their limits from the wait's start when unset, from the content's end with `true`; the screen is parked unseen in
|
|
82
|
+
the stack's view by `ANNParking`, `stackPark:`); the content's end, content there already too, waits for the calls
|
|
83
|
+
JavaScript made by then (`ANNJavaScriptCalls`: the JavaScript thread, the module's queue, the main queue), so the
|
|
84
|
+
options of the screen's first commit are in the bar; with `false` nothing. Then it renders the node, drives the appearance of Home and Profile (`+[ANNAppearanceDispatcher driveAppearanceOf:]`), and calls
|
|
81
85
|
`+[ANNAppearanceDispatcher beginTransitionShowing:]`. Profile's `ComponentWillAppear` is sent now (held until its
|
|
82
86
|
content mounted, 0.5 s at most). `stackChange:` pushes in UIKit; with an own animation `ANNTransitionPlan` and
|
|
83
87
|
`ANNStackAnimatingDelegate` give UIKit the `ANNTransitionAnimator`.
|
|
@@ -85,7 +89,7 @@ dispatcher.
|
|
|
85
89
|
`+[ANNAppearanceDispatcher endTransitionHiding:showing:events:answer:value:]`. In this order: `ComponentDidDisappear`
|
|
86
90
|
of Home, `ComponentDidAppear` of Profile, `ANN.NavigationStateChanged` (`+[ANNNavigationState updateNow]`), the
|
|
87
91
|
transition's `events`, then `-[ANNCommandAnswer resolve:]`: the state again if it changed
|
|
88
|
-
(`
|
|
92
|
+
(`reportBeforeAnswerOf:`), the promise with Profile's id, then `ANN.CommandCompleted`. `finish:` frees the queue for
|
|
89
93
|
the next operation.
|
|
90
94
|
|
|
91
95
|
Events go out through `ANNEventEmitter` on the main queue and reach JS through `ANNTurboEventEmitter`.
|
|
@@ -116,13 +120,14 @@ Each of these owns one rule. Call it; do not copy it.
|
|
|
116
120
|
- `ANNCommandAnswer`: The answer: state, then promise, then `ANN.CommandCompleted`; once; silent for a JavaScript that went. Called by: Every coordinator and queue.
|
|
117
121
|
- `ANNNodeRegistry`: Every node by id, weakly. Filled by the factory, cleared at a reload. Called by: Commands, modules, `findViewController:`.
|
|
118
122
|
- `ANNLayoutFactory`: The core code that names module classes (`ANNSplitViewController`, `ANNSideMenuViewController`, `ANNArrangementViewController`); `ANNSideMenuPresenter` is the one other file that does. One maker per layout type; registers each node. Called by: Every command that shows a layout.
|
|
119
|
-
- `ANNOptionsCache`: The one invalidation point of resolved options. Called by: The
|
|
120
|
-
- `ANNNodeHelper resolveOptionsWithDefault`: What a node shows with: defaults, parents' own options, the node's `resolveOptions` (own, then the current child down). Cached, read-only. Called by: Presenters, coordinators.
|
|
123
|
+
- `ANNOptionsCache`: The one invalidation point of resolved options. Called by: The setters of `ANNNodeHelper.options` and of its user layer (`setUserChoice:forKey:`), and `ANNDefaultOptions`, only.
|
|
124
|
+
- `ANNNodeHelper resolveOptionsWithDefault`: What a node shows with: defaults, parents' own options (each with its user layer), the node's `resolveOptions` (own, its user layer, then the current child down). Cached, read-only. Called by: Presenters, coordinators.
|
|
121
125
|
- `ANNAppearanceDispatcher`: The only sender of `ComponentWillAppear`, `ComponentDidAppear`, `ComponentDidDisappear`; the end-of-transition order; the events of a node that goes. Called by: Screens, queues, coordinators.
|
|
122
126
|
- `ANNNavigationState`: The visible screen and the nearest tabs it is in (the last tabs met walking down); coalesced updates, holds, the state before an answer. Called by: The dispatcher, `ANNCommandAnswer`, `ANNRootCoordinator`.
|
|
123
127
|
- `ANNScreenReadiness`: The signals of a screen's safe area view (has one, laid out, content rendered), sticky, by screen id. Called by: `ANNSafeAreaViewComponentView`, `ANNReactView`.
|
|
124
128
|
- `ANNRenderWait`: The one wait for the render of what a command shows: content, bar components, limits, the unknown-content rule. Called by: Root, stack queue, modals, overlays, `showInColumn`.
|
|
125
129
|
- `ANNParking`: Where a view waits for its content unseen: the window, a stack's view, a preview's host view. Called by: Root, stack, modals, overlays, `ANNPreviewPush`.
|
|
130
|
+
- `ANNScreenPlace`: The safe area a parked screen lays out with, measured where it will show on top of a stack. Called by: `ANNStackController` (`stackPark:`), `ANNSplitViewCommands` (`showInColumn`), through `placeWhileParked` of the screen.
|
|
126
131
|
- `ANNPendingLayers`: Layers that wait to show (a pending root, a modal, an overlay); a node inside counts as off screen. Called by: The layer's owner; nodes ask.
|
|
127
132
|
- `ANNShownScreens`: "What a node shows changed": tells the pending layer, the modal hold and the system UI, in that order. Called by: Every container that changes what it shows.
|
|
128
133
|
- `ANNModalCoordinator`, `ANNModal`: One record per modal, its states and waiting commands. Called by: Commands, root, state, repaint, reload.
|
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
#import "ANNCommandAnswer.h"
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
|
+
#import "ANNCommandContract.h"
|
|
3
4
|
#import "ANNErrorHandler.h"
|
|
4
5
|
#import "ANNErrorTexts.h"
|
|
5
6
|
#import "ANNNavigationState.h"
|
|
6
7
|
#import "ANNReloadCoordinator.h"
|
|
7
8
|
#import "ApexNavigation+Internal.h"
|
|
8
9
|
|
|
9
|
-
// The commands that send `ANN.CommandCompleted` once they resolved: every one that shows or dismisses, not
|
|
10
|
-
// `setColumnShown`, `preparePush` or `getLaunchLink`.
|
|
11
|
-
static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
12
|
-
static NSSet<NSString *> *names;
|
|
13
|
-
static dispatch_once_t once;
|
|
14
|
-
dispatch_once(&once, ^{
|
|
15
|
-
names = [NSSet setWithArray:@[
|
|
16
|
-
@"setRoot", @"push", @"pop", @"popTo", @"popToRoot", @"setStackRoot", @"showInColumn",
|
|
17
|
-
@"showModal", @"dismissModal", @"dismissAllModals", @"showOverlay", @"dismissOverlay",
|
|
18
|
-
@"dismissAllOverlays"
|
|
19
|
-
]];
|
|
20
|
-
});
|
|
21
|
-
return names;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
10
|
@implementation ANNCommandAnswer {
|
|
25
11
|
RCTPromiseResolveBlock _resolve;
|
|
26
12
|
RCTPromiseRejectBlock _reject;
|
|
@@ -72,14 +58,13 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
72
58
|
RCTPromiseResolveBlock resolve = _resolve;
|
|
73
59
|
_resolve = nil;
|
|
74
60
|
_reject = nil;
|
|
75
|
-
// A command that may change what shows (
|
|
61
|
+
// A command that may change what shows (`ANNCommandContract`): the state goes before the promise, so that
|
|
76
62
|
// `getState()` after `await` has the result.
|
|
77
|
-
BOOL changesWhatShows =
|
|
78
|
-
[_commandName isEqualToString:@"setColumnShown"];
|
|
63
|
+
BOOL changesWhatShows = ANNCommandReportsState(_commandName);
|
|
79
64
|
if (changesWhatShows) {
|
|
80
65
|
// The command did its work: a failure of the state step costs only the state, never the answer.
|
|
81
66
|
@try {
|
|
82
|
-
[ANNNavigationState
|
|
67
|
+
[ANNNavigationState reportBeforeAnswerOf:_commandName];
|
|
83
68
|
} @catch (NSException *exception) {
|
|
84
69
|
NSString *reason = exception.reason.length > 0 ? exception.reason : exception.name;
|
|
85
70
|
[ANNErrorHandler logFailureOf:_commandName
|
|
@@ -88,7 +73,7 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
88
73
|
}
|
|
89
74
|
}
|
|
90
75
|
[ANNErrorHandler resolve:resolve of:_commandName value:value];
|
|
91
|
-
if (_commandId &&
|
|
76
|
+
if (_commandId && ANNCommandSendsCompleted(_commandName)) {
|
|
92
77
|
double completionTime = floor(NSDate.date.timeIntervalSince1970 * 1000);
|
|
93
78
|
[ApexNavigation.eventEmitter sendCommandCompleted:_commandName
|
|
94
79
|
commandId:_commandId
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/commands.json: change that file and run the script again.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
/// Whether the navigation state goes before the answer of the command `name`: every command that may change what
|
|
8
|
+
/// shows. Any thread.
|
|
9
|
+
FOUNDATION_EXPORT BOOL ANNCommandReportsState(NSString *name);
|
|
10
|
+
|
|
11
|
+
/// Whether `ANN.CommandCompleted` follows the answer of the command `name`: every command that shows or dismisses.
|
|
12
|
+
/// Any thread.
|
|
13
|
+
FOUNDATION_EXPORT BOOL ANNCommandSendsCompleted(NSString *name);
|
|
14
|
+
|
|
15
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/commands.json: change that file and run the script again.
|
|
2
|
+
|
|
3
|
+
#import "ANNCommandContract.h"
|
|
4
|
+
|
|
5
|
+
// What each command resolves with:
|
|
6
|
+
// - `setRoot`: the id of the root, once the root and the layout's modals and overlays show.
|
|
7
|
+
// - `push`: the id of the screen that shows; of a pair, the pair's id.
|
|
8
|
+
// - `preparePush`: the frame `{ width, height, padding }` of the prepared screen, or null; always
|
|
9
|
+
// null on iOS.
|
|
10
|
+
// - `pop`: the id of the screen that went.
|
|
11
|
+
// - `popTo`: the id of the screen it went back to.
|
|
12
|
+
// - `popToRoot`: the id of the first screen of the stack.
|
|
13
|
+
// - `setStackRoot`: the id of the new top screen.
|
|
14
|
+
// - `showModal`: the id of the modal's root.
|
|
15
|
+
// - `dismissModal`: the id of the modal's root.
|
|
16
|
+
// - `dismissAllModals`: an empty string.
|
|
17
|
+
// - `showOverlay`: the id of the overlay's root.
|
|
18
|
+
// - `dismissOverlay`: the id of the overlay's root.
|
|
19
|
+
// - `dismissAllOverlays`: an empty string.
|
|
20
|
+
// - `showInColumn`: the id of the screen that shows.
|
|
21
|
+
// - `setColumnShown`: nothing once the columns stopped moving (`undefined` on iOS, `null` on
|
|
22
|
+
// Android).
|
|
23
|
+
// - `getLaunchLink`: the link that launched the app, or null.
|
|
24
|
+
|
|
25
|
+
static NSSet<NSString *> *ANNStateCommandNames(void) {
|
|
26
|
+
static NSSet<NSString *> *names;
|
|
27
|
+
static dispatch_once_t once;
|
|
28
|
+
dispatch_once(&once, ^{
|
|
29
|
+
names = [NSSet setWithArray:@[
|
|
30
|
+
@"setRoot",
|
|
31
|
+
@"push",
|
|
32
|
+
@"pop",
|
|
33
|
+
@"popTo",
|
|
34
|
+
@"popToRoot",
|
|
35
|
+
@"setStackRoot",
|
|
36
|
+
@"showModal",
|
|
37
|
+
@"dismissModal",
|
|
38
|
+
@"dismissAllModals",
|
|
39
|
+
@"showOverlay",
|
|
40
|
+
@"dismissOverlay",
|
|
41
|
+
@"dismissAllOverlays",
|
|
42
|
+
@"showInColumn",
|
|
43
|
+
@"setColumnShown",
|
|
44
|
+
]];
|
|
45
|
+
});
|
|
46
|
+
return names;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
50
|
+
static NSSet<NSString *> *names;
|
|
51
|
+
static dispatch_once_t once;
|
|
52
|
+
dispatch_once(&once, ^{
|
|
53
|
+
names = [NSSet setWithArray:@[
|
|
54
|
+
@"setRoot",
|
|
55
|
+
@"push",
|
|
56
|
+
@"pop",
|
|
57
|
+
@"popTo",
|
|
58
|
+
@"popToRoot",
|
|
59
|
+
@"setStackRoot",
|
|
60
|
+
@"showModal",
|
|
61
|
+
@"dismissModal",
|
|
62
|
+
@"dismissAllModals",
|
|
63
|
+
@"showOverlay",
|
|
64
|
+
@"dismissOverlay",
|
|
65
|
+
@"dismissAllOverlays",
|
|
66
|
+
@"showInColumn",
|
|
67
|
+
]];
|
|
68
|
+
});
|
|
69
|
+
return names;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
BOOL ANNCommandReportsState(NSString *name) {
|
|
73
|
+
return [ANNStateCommandNames() containsObject:name];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
BOOL ANNCommandSendsCompleted(NSString *name) {
|
|
77
|
+
return [ANNCompletedCommandNames() containsObject:name];
|
|
78
|
+
}
|
|
@@ -305,7 +305,7 @@ static BOOL ANNIsInsideNode(UIViewController *screen, UIViewController *node) {
|
|
|
305
305
|
for (UIViewController *node in shown) {
|
|
306
306
|
[self nodeDidAppear:node];
|
|
307
307
|
}
|
|
308
|
-
[ANNNavigationState
|
|
308
|
+
[ANNNavigationState updateNowFor:answer.commandName];
|
|
309
309
|
if (events) {
|
|
310
310
|
events();
|
|
311
311
|
}
|