react-native-navigation 8.7.5 → 8.7.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/android/ARCHITECTURE.md +458 -0
- package/android/src/main/java/com/reactnativenavigation/react/NavigationModule.java +2 -2
- package/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/ButtonBar.kt +2 -0
- package/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/IconBackgroundDrawable.kt +20 -17
- package/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleAndButtonsMeasurer.kt +2 -1
- package/android/src/main/java/com/reactnativenavigation/views/stack/topbar/titlebar/TitleBarReactView.kt +7 -10
- package/android/src/main/res/values/styles.xml +2 -0
- package/ios/ARCHITECTURE.md +416 -0
- package/ios/BottomTabsAfterInitialTabAttacher.mm +31 -0
- package/ios/BottomTabsAppearancePresenter.mm +69 -32
- package/ios/BottomTabsBasePresenter.mm +5 -4
- package/ios/RCTHelpers.mm +3 -1
- package/ios/RNNAppDelegate.mm +10 -0
- package/ios/RNNBottomTabsController.mm +19 -6
- package/ios/RNNBottomTabsOptions.mm +5 -1
- package/ios/RNNBridgeManager.mm +2 -0
- package/ios/RNNComponentViewController.mm +4 -2
- package/ios/RNNModalHostViewManagerHandler.h +5 -0
- package/ios/RNNModalHostViewManagerHandler.mm +2 -0
- package/ios/RNNOverlayWindow.mm +1 -2
- package/ios/RNNScrollEdgeAppearanceOptions.h +2 -0
- package/ios/RNNScrollEdgeAppearanceOptions.mm +2 -0
- package/ios/TopBarAppearancePresenter.mm +35 -0
- package/lib/module/ARCHITECTURE.md +301 -0
- package/lib/module/interfaces/Options.js.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +29 -0
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/ARCHITECTURE.md +301 -0
- package/src/interfaces/Options.ts +30 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# iOS Native Architecture
|
|
2
|
+
|
|
3
|
+
The iOS implementation provides native navigation using UIKit view controllers, coordinated through a bridge module that receives commands from JavaScript.
|
|
4
|
+
|
|
5
|
+
## App Integration
|
|
6
|
+
|
|
7
|
+
### RNNAppDelegate
|
|
8
|
+
**Files**: `RNNAppDelegate.h/mm`
|
|
9
|
+
|
|
10
|
+
Base class that user's AppDelegate must extend. Handles React Native and navigation initialization:
|
|
11
|
+
|
|
12
|
+
```objc
|
|
13
|
+
// AppDelegate.h - User extends RNNAppDelegate
|
|
14
|
+
#import "RNNAppDelegate.h"
|
|
15
|
+
@interface AppDelegate : RNNAppDelegate
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
// AppDelegate.m - User implements sourceURLForBridge
|
|
19
|
+
@implementation AppDelegate
|
|
20
|
+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
|
|
21
|
+
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
|
22
|
+
}
|
|
23
|
+
@end
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**What RNNAppDelegate does:**
|
|
27
|
+
- Sets up React Native feature flags (Fabric, TurboModules, Bridgeless)
|
|
28
|
+
- Creates `RCTRootViewFactory` and `ReactHost`
|
|
29
|
+
- Calls `[ReactNativeNavigation bootstrapWithHost:]` to initialize navigation
|
|
30
|
+
- Handles RN version differences (0.77, 0.78, 0.79+) via compile-time macros
|
|
31
|
+
|
|
32
|
+
### ReactNativeNavigation Bootstrap
|
|
33
|
+
**File**: `ReactNativeNavigation.h/mm`
|
|
34
|
+
|
|
35
|
+
Public API for native initialization:
|
|
36
|
+
|
|
37
|
+
```objc
|
|
38
|
+
// New architecture (0.77+)
|
|
39
|
+
[ReactNativeNavigation bootstrapWithHost:reactHost];
|
|
40
|
+
|
|
41
|
+
// Legacy bridge
|
|
42
|
+
[ReactNativeNavigation bootstrapWithBridge:bridge];
|
|
43
|
+
|
|
44
|
+
// Register native screens
|
|
45
|
+
[ReactNativeNavigation registerExternalComponent:@"NativeScreen"
|
|
46
|
+
callback:^(NSDictionary *props, RCTBridge *bridge) {
|
|
47
|
+
return [[MyNativeVC alloc] init];
|
|
48
|
+
}];
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Autolink Script (`npx rnn-link`)
|
|
52
|
+
|
|
53
|
+
The `autolink/postlink/postLinkIOS.js` script automates setup:
|
|
54
|
+
|
|
55
|
+
| Linker | What it does |
|
|
56
|
+
|--------|--------------|
|
|
57
|
+
| `AppDelegateLinker` | Changes AppDelegate to extend `RNNAppDelegate`, imports ReactNativeNavigation, removes RCTRootView setup |
|
|
58
|
+
| `PodfileLinker` | Adds required pods configuration |
|
|
59
|
+
| `PlistLinker` | Updates Info.plist if needed |
|
|
60
|
+
|
|
61
|
+
**AppDelegateLinker transformations:**
|
|
62
|
+
- Swift: `class AppDelegate: RCTAppDelegate` → `class AppDelegate: RNNAppDelegate`
|
|
63
|
+
- Obj-C header: `@interface AppDelegate : RCTAppDelegate` → `@interface AppDelegate : RNNAppDelegate`
|
|
64
|
+
- Imports `<ReactNativeNavigation/ReactNativeNavigation.h>`
|
|
65
|
+
- Removes manual RCTRootView/window setup (navigation manages the window)
|
|
66
|
+
|
|
67
|
+
## Directory Structure
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
ios/
|
|
71
|
+
├── RNNBridgeModule.h/mm # Bridge entry point (legacy architecture)
|
|
72
|
+
├── RNNBridgeManager.h/mm # Bridge initialization
|
|
73
|
+
├── RNNCommandsHandler.h/mm # Command dispatcher
|
|
74
|
+
├── RNNEventEmitter.h/mm # Event emission to JS
|
|
75
|
+
├── RNNLayoutManager.h/mm # View controller tracking
|
|
76
|
+
├── RNNLayoutNode.h/mm # Layout tree parsing
|
|
77
|
+
├── RNNNavigationOptions.h/mm # Options model
|
|
78
|
+
├── RNNComponentViewController.h/mm # React component screen
|
|
79
|
+
├── RNNStackController.h/mm # Stack navigation
|
|
80
|
+
├── RNNBottomTabsController.h/mm # Tab navigation
|
|
81
|
+
├── RNNSideMenuViewController.h/mm # Side menu
|
|
82
|
+
├── RNNTopTabsViewController.h/mm # Top tabs
|
|
83
|
+
├── RNNSplitViewController.h/mm # Split view (iPad)
|
|
84
|
+
├── RNNModalManager.h/mm # Modal presentation
|
|
85
|
+
├── RNNOverlayManager.h/mm # Overlay management
|
|
86
|
+
├── TurboModules/ # New architecture entry points
|
|
87
|
+
│ ├── RNNTurboModule.h/mm # TurboModule spec implementation
|
|
88
|
+
│ ├── RNNTurboManager.h/mm # Manager for host, window, external components
|
|
89
|
+
│ ├── RNNTurboCommandsHandler.h/mm # TurboModule command routing
|
|
90
|
+
│ └── RNNTurboEventEmitter.h/mm # Event emission for new arch
|
|
91
|
+
├── Utils/ # Utility classes
|
|
92
|
+
├── RNNSideMenu/ # MMDrawerController integration
|
|
93
|
+
└── ReactNativeNavigation.xcodeproj/
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Bridge Architecture
|
|
97
|
+
|
|
98
|
+
The library supports both the legacy bridge and new TurboModule architecture:
|
|
99
|
+
- **Legacy (Bridge)**: `RNNBridgeModule` receives commands via `RCT_EXPORT_METHOD`
|
|
100
|
+
- **New Architecture**: `RNNTurboModule` (in `TurboModules/`) receives commands directly
|
|
101
|
+
|
|
102
|
+
Both entry points delegate to `RNNCommandsHandler` for command execution.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
┌────────────────────────────────────────────────────────────┐
|
|
106
|
+
│ JavaScript (TurboModule) │
|
|
107
|
+
└─────────────────────────┬──────────────────────────────────┘
|
|
108
|
+
│
|
|
109
|
+
┌─────────────────────────▼──────────────────────────────────┐
|
|
110
|
+
│ RNNBridgeModule (bridge) / RNNTurboModule (new arch) │
|
|
111
|
+
│ - setRoot, push, pop, showModal, dismissModal, etc. │
|
|
112
|
+
└─────────────────────────┬──────────────────────────────────┘
|
|
113
|
+
│
|
|
114
|
+
┌─────────────────────────▼──────────────────────────────────┐
|
|
115
|
+
│ RNNCommandsHandler │
|
|
116
|
+
│ - Validates commands, manages layout lifecycle │
|
|
117
|
+
│ - Coordinates with managers │
|
|
118
|
+
└──────┬──────────────┬──────────────┬──────────────┬────────┘
|
|
119
|
+
│ │ │ │
|
|
120
|
+
┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐ ┌─────▼──────┐
|
|
121
|
+
│RNNLayout │ │RNNModal │ │RNNOverlay │ │RNNViewController│
|
|
122
|
+
│Manager │ │Manager │ │Manager │ │Factory │
|
|
123
|
+
└────────────┘ └────────────┘ └────────────┘ └────────────┘
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## View Controller Hierarchy
|
|
127
|
+
|
|
128
|
+
All navigation view controllers conform to `RNNLayoutProtocol`:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
UIViewController
|
|
132
|
+
├── RNNComponentViewController # React component screen
|
|
133
|
+
├── RNNExternalViewController # Native screen wrapper
|
|
134
|
+
└── RNNSplashScreenViewController # Launch screen
|
|
135
|
+
|
|
136
|
+
UINavigationController
|
|
137
|
+
└── RNNStackController # Stack navigation
|
|
138
|
+
|
|
139
|
+
UITabBarController
|
|
140
|
+
└── RNNBottomTabsController # Bottom tabs
|
|
141
|
+
|
|
142
|
+
UIViewController (custom)
|
|
143
|
+
├── RNNTopTabsViewController # Horizontal top tabs
|
|
144
|
+
└── RNNSideMenuChildViewController # Drawer child
|
|
145
|
+
|
|
146
|
+
MMDrawerController
|
|
147
|
+
└── RNNSideMenuViewController # Side menu/drawer
|
|
148
|
+
|
|
149
|
+
UISplitViewController
|
|
150
|
+
└── RNNSplitViewController # Master-detail (iPad)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Core Classes
|
|
154
|
+
|
|
155
|
+
### RNNBridgeModule
|
|
156
|
+
Entry point for JavaScript commands (legacy architecture). Exports methods via `RCT_EXPORT_METHOD`:
|
|
157
|
+
|
|
158
|
+
```objc
|
|
159
|
+
RCT_EXPORT_METHOD(setRoot:(NSString*)commandId layout:(NSDictionary*)layout
|
|
160
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
161
|
+
rejecter:(RCTPromiseRejectBlock)reject);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`RNNBridgeModule` returns the main queue as its `methodQueue`; `RNNTurboModule` uses `RCTExecuteOnMainQueue` for each command to ensure UI operations run on the main thread.
|
|
165
|
+
|
|
166
|
+
### RNNBridgeManager
|
|
167
|
+
Initializes bridge infrastructure:
|
|
168
|
+
- Creates RNNLayoutManager, RNNModalManager, RNNOverlayManager
|
|
169
|
+
- Creates RNNViewControllerFactory, RNNCommandsHandler
|
|
170
|
+
- Provides extra modules to React bridge
|
|
171
|
+
- Handles JavaScript reload events
|
|
172
|
+
|
|
173
|
+
### RNNCommandsHandler
|
|
174
|
+
Central command dispatcher implementing all navigation operations:
|
|
175
|
+
- `setRoot:` - Replace root view controller
|
|
176
|
+
- `push:componentId:layout:` - Push to stack
|
|
177
|
+
- `pop:componentId:` - Pop from stack
|
|
178
|
+
- `showModal:` - Present modal
|
|
179
|
+
- `dismissModal:` - Dismiss modal
|
|
180
|
+
- `showOverlay:` - Show overlay window
|
|
181
|
+
- `dismissOverlay:` - Hide overlay
|
|
182
|
+
|
|
183
|
+
### RNNLayoutManager
|
|
184
|
+
Tracks active view controllers:
|
|
185
|
+
- `addPendingViewController:` - Register during creation
|
|
186
|
+
- `removePendingViewController:` - Cleanup after presentation
|
|
187
|
+
- `findComponentForId:` - Lookup by componentId
|
|
188
|
+
|
|
189
|
+
### RNNLayoutNode
|
|
190
|
+
Parses JSON layout from JavaScript:
|
|
191
|
+
- Determines type via predicates: `isComponent`, `isStack`, `isTabs`, etc.
|
|
192
|
+
- Holds `type`, `nodeId`, `data` (options), `children`
|
|
193
|
+
|
|
194
|
+
### RNNViewControllerFactory
|
|
195
|
+
Creates view controllers from RNNLayoutNode:
|
|
196
|
+
- `createStack:` → RNNStackController
|
|
197
|
+
- `createBottomTabs:` → RNNBottomTabsController
|
|
198
|
+
- `createComponent:` → RNNComponentViewController
|
|
199
|
+
- `createSideMenu:` → RNNSideMenuViewController
|
|
200
|
+
|
|
201
|
+
## RNNLayoutProtocol
|
|
202
|
+
|
|
203
|
+
Protocol all navigation controllers implement:
|
|
204
|
+
|
|
205
|
+
```objc
|
|
206
|
+
@protocol RNNLayoutProtocol
|
|
207
|
+
- (instancetype)initWithLayoutInfo:(RNNLayoutInfo*)layoutInfo
|
|
208
|
+
creator:(id<RNNComponentViewCreator>)creator
|
|
209
|
+
options:(RNNNavigationOptions*)options
|
|
210
|
+
defaultOptions:(RNNNavigationOptions*)defaultOptions
|
|
211
|
+
presenter:(RNNBasePresenter*)presenter
|
|
212
|
+
eventEmitter:(RNNEventEmitter*)eventEmitter
|
|
213
|
+
childViewControllers:(NSArray*)childViewControllers;
|
|
214
|
+
|
|
215
|
+
- (void)render;
|
|
216
|
+
- (UIViewController*)getCurrentChild;
|
|
217
|
+
- (void)mergeOptions:(RNNNavigationOptions*)options;
|
|
218
|
+
- (RNNNavigationOptions*)resolveOptions;
|
|
219
|
+
- (void)setDefaultOptions:(RNNNavigationOptions*)defaultOptions;
|
|
220
|
+
- (void)onChildWillAppear;
|
|
221
|
+
- (void)readyForPresentation;
|
|
222
|
+
- (CGFloat)getTopBarHeight;
|
|
223
|
+
- (CGFloat)getBottomTabsHeight;
|
|
224
|
+
@end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Additional methods provided via `UIViewController+LayoutProtocol` category:
|
|
228
|
+
- `destroy`, `topMostViewController`, `stack`
|
|
229
|
+
- `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
|
|
230
|
+
- `screenPopped`, `prepareForTransition`
|
|
231
|
+
- `resolveOptionsWithDefault`, `mergeChildOptions:child:`
|
|
232
|
+
|
|
233
|
+
## Presenter Pattern
|
|
234
|
+
|
|
235
|
+
Each view controller type has a corresponding presenter that applies options:
|
|
236
|
+
|
|
237
|
+
| Controller | Presenter |
|
|
238
|
+
|------------|-----------|
|
|
239
|
+
| RNNComponentViewController | RNNComponentPresenter |
|
|
240
|
+
| RNNStackController | RNNStackPresenter + TopBarPresenter |
|
|
241
|
+
| RNNBottomTabsController | RNNBottomTabsPresenter + BottomTabPresenter |
|
|
242
|
+
| RNNSideMenuViewController | RNNSideMenuPresenter |
|
|
243
|
+
| RNNSplitViewController | RNNSplitViewControllerPresenter |
|
|
244
|
+
|
|
245
|
+
### RNNBasePresenter
|
|
246
|
+
Base class with lifecycle methods:
|
|
247
|
+
- `applyOptionsOnInit:` - Initial setup
|
|
248
|
+
- `applyOptions:` - Apply current options
|
|
249
|
+
- `mergeOptions:resolvedOptions:` - Runtime updates
|
|
250
|
+
- `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
|
|
251
|
+
|
|
252
|
+
## React View Integration
|
|
253
|
+
|
|
254
|
+
### RNNReactView
|
|
255
|
+
Wraps RCTRootView (legacy) or RCTSurfaceHostingView (new architecture):
|
|
256
|
+
- Implements `RNNComponentProtocol`
|
|
257
|
+
- Manages `componentId`, `componentType`, `moduleName`
|
|
258
|
+
- Lifecycle: `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
|
|
259
|
+
|
|
260
|
+
### RNNReactRootViewCreator
|
|
261
|
+
Creates RNNReactView instances:
|
|
262
|
+
- Supports component types: Component, TopBarTitle, TopBarButton, TopBarBackground
|
|
263
|
+
- Handles size flexibility for flexible layouts
|
|
264
|
+
|
|
265
|
+
### RNNReactComponentRegistry
|
|
266
|
+
Caches created React component instances:
|
|
267
|
+
- `createComponentIfNotExists:parentComponentId:componentType:reactViewReadyBlock:`
|
|
268
|
+
- `removeComponent:`, `clearComponentsForParentId:`
|
|
269
|
+
|
|
270
|
+
## Options System
|
|
271
|
+
|
|
272
|
+
### RNNNavigationOptions
|
|
273
|
+
Master options object containing all configuration:
|
|
274
|
+
|
|
275
|
+
```objc
|
|
276
|
+
@interface RNNNavigationOptions : RNNOptions
|
|
277
|
+
@property RNNTopBarOptions* topBar;
|
|
278
|
+
@property RNNBottomTabsOptions* bottomTabs;
|
|
279
|
+
@property RNNBottomTabOptions* bottomTab;
|
|
280
|
+
@property RNNTopTabsOptions* topTabs;
|
|
281
|
+
@property RNNSideMenuOptions* sideMenu;
|
|
282
|
+
@property RNNOverlayOptions* overlay;
|
|
283
|
+
@property RNNAnimationsOptions* animations;
|
|
284
|
+
@property RNNStatusBarOptions* statusBar;
|
|
285
|
+
@property RNNLayoutOptions* layout;
|
|
286
|
+
@property RNNModalOptions* modal;
|
|
287
|
+
@property RNNPreviewOptions* preview;
|
|
288
|
+
@property RNNSplitViewOptions* splitView;
|
|
289
|
+
@end
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Options Resolution
|
|
293
|
+
Options merge in hierarchy:
|
|
294
|
+
1. Component's own options
|
|
295
|
+
2. Parent controller options (loop through chain)
|
|
296
|
+
3. Default options (from `setDefaultOptions`)
|
|
297
|
+
|
|
298
|
+
## Event Emission
|
|
299
|
+
|
|
300
|
+
### RNNEventEmitter
|
|
301
|
+
Sends events to JavaScript via RCTEventEmitter:
|
|
302
|
+
|
|
303
|
+
| Event | Method |
|
|
304
|
+
|-------|--------|
|
|
305
|
+
| Component lifecycle | `sendComponentWillAppear:`, `sendComponentDidAppear:`, `sendComponentDidDisappear:` |
|
|
306
|
+
| Button press | `sendOnNavigationButtonPressed:buttonId:` |
|
|
307
|
+
| Command completion | `sendOnNavigationCommandCompletion:commandId:` |
|
|
308
|
+
| Tab events | `sendBottomTabSelected:unselected:`, `sendBottomTabLongPressed:`, `sendBottomTabPressed:` |
|
|
309
|
+
| Modal events | `sendModalsDismissedEvent:numberOfModalsDismissed:`, `sendModalAttemptedToDismissEvent:` |
|
|
310
|
+
| Screen events | `sendScreenPoppedEvent:` |
|
|
311
|
+
| Search | `sendOnSearchBarUpdated:text:isFocused:`, `sendOnSearchBarCancelPressed:` |
|
|
312
|
+
| Preview | `sendOnPreviewCompleted:previewComponentId:` |
|
|
313
|
+
|
|
314
|
+
### RNNBridgeEventEmitter
|
|
315
|
+
Concrete implementation that sends `onAppLaunched` on initialization.
|
|
316
|
+
|
|
317
|
+
## Modal & Overlay Management
|
|
318
|
+
|
|
319
|
+
### RNNModalManager
|
|
320
|
+
- Tracks presented modals array
|
|
321
|
+
- Supports custom transition animations via ScreenAnimationController
|
|
322
|
+
- Handles presentationController delegate for adaptive presentation
|
|
323
|
+
|
|
324
|
+
### RNNOverlayManager
|
|
325
|
+
- Manages UIWindow instances for overlays
|
|
326
|
+
- Each overlay gets its own RNNOverlayWindow
|
|
327
|
+
- Maintains previous window reference for restoration
|
|
328
|
+
- Controls window level and accessibility
|
|
329
|
+
|
|
330
|
+
## Animation System
|
|
331
|
+
|
|
332
|
+
### ScreenAnimationController
|
|
333
|
+
Implements `UIViewControllerAnimatedTransitioning`:
|
|
334
|
+
- Custom push/pop/modal transitions
|
|
335
|
+
- Content transitions (RNNEnterExitAnimation)
|
|
336
|
+
- Element transitions (ElementTransitionOptions)
|
|
337
|
+
- Shared element transitions (SharedElementTransitionOptions)
|
|
338
|
+
|
|
339
|
+
### Element Animators
|
|
340
|
+
- `ElementAnimator` - Individual element animations
|
|
341
|
+
- `SharedElementAnimator` - Cross-screen shared elements
|
|
342
|
+
- Transition types: Alpha, Scale, Translation, Frame, Bounds, Color, CornerRadius
|
|
343
|
+
|
|
344
|
+
### RNNSetRootAnimator
|
|
345
|
+
Animates window root replacement using CABasicAnimation.
|
|
346
|
+
|
|
347
|
+
## Navigation Types
|
|
348
|
+
|
|
349
|
+
### Stack Navigation (RNNStackController)
|
|
350
|
+
- Subclass of UINavigationController
|
|
351
|
+
- Push/pop with custom animations
|
|
352
|
+
- Back button customization
|
|
353
|
+
- Uses StackControllerDelegate for bar delegate handling
|
|
354
|
+
|
|
355
|
+
```objc
|
|
356
|
+
// Extension methods
|
|
357
|
+
@interface UINavigationController (RNNCommands)
|
|
358
|
+
- (void)push:(UIViewController*)vc onTop:(UIViewController*)onTopVC
|
|
359
|
+
animated:(BOOL)animated completion:(void(^)(void))completion rejection:(RCTPromiseRejectBlock)rejection;
|
|
360
|
+
- (void)popAnimated:(BOOL)animated completion:(void(^)(NSString*))completion rejection:(RCTPromiseRejectBlock)rejection;
|
|
361
|
+
@end
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Bottom Tabs (RNNBottomTabsController)
|
|
365
|
+
- Subclass of UITabBarController
|
|
366
|
+
- Tab attachment modes: Together, OnSwitchToTab, AfterInitialTab
|
|
367
|
+
- Long-press gesture support
|
|
368
|
+
- Badge and dot indicator support
|
|
369
|
+
|
|
370
|
+
### Side Menu (RNNSideMenuViewController)
|
|
371
|
+
- Uses MMDrawerController (third-party)
|
|
372
|
+
- Center, left, right child containers
|
|
373
|
+
- Configurable open modes and widths
|
|
374
|
+
|
|
375
|
+
### Top Tabs (RNNTopTabsViewController)
|
|
376
|
+
- Custom horizontal tab implementation
|
|
377
|
+
- Not based on UITabBarController
|
|
378
|
+
- Manual content switching
|
|
379
|
+
|
|
380
|
+
### Split View (RNNSplitViewController)
|
|
381
|
+
- UISplitViewController subclass
|
|
382
|
+
- Master-detail for iPad
|
|
383
|
+
|
|
384
|
+
## External Components
|
|
385
|
+
|
|
386
|
+
### RNNExternalComponentStore
|
|
387
|
+
Registry for native (non-React) view controllers.
|
|
388
|
+
|
|
389
|
+
### RNNExternalViewController
|
|
390
|
+
Wraps native UIViewController for integration:
|
|
391
|
+
|
|
392
|
+
```objc
|
|
393
|
+
[ReactNativeNavigation registerExternalComponent:@"NativeScreen"
|
|
394
|
+
callback:^(NSDictionary *props, RCTBridge *bridge) {
|
|
395
|
+
return [[MyNativeViewController alloc] init];
|
|
396
|
+
}];
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Key Files Reference
|
|
400
|
+
|
|
401
|
+
| File | Purpose |
|
|
402
|
+
|------|---------|
|
|
403
|
+
| `RNNBridgeModule.h/mm` | Bridge entry point (legacy) |
|
|
404
|
+
| `TurboModules/RNNTurboModule.mm` | TurboModule entry point (new arch) |
|
|
405
|
+
| `TurboModules/RNNTurboCommandsHandler.mm` | TurboModule command routing |
|
|
406
|
+
| `RNNCommandsHandler.h/mm` | Command execution |
|
|
407
|
+
| `RNNLayoutManager.h/mm` | Controller tracking |
|
|
408
|
+
| `RNNViewControllerFactory.h/mm` | Controller creation |
|
|
409
|
+
| `RNNComponentViewController.h/mm` | React screen |
|
|
410
|
+
| `RNNStackController.h/mm` | Stack navigation |
|
|
411
|
+
| `RNNBottomTabsController.h/mm` | Tab navigation |
|
|
412
|
+
| `RNNNavigationOptions.h/mm` | Options model |
|
|
413
|
+
| `RNNBasePresenter.h/mm` | Presenter base |
|
|
414
|
+
| `TopBarPresenter.h/mm` | Top bar styling |
|
|
415
|
+
| `RNNReactView.h/mm` | React view wrapper |
|
|
416
|
+
| `ScreenAnimationController.h/mm` | Custom transitions |
|
|
@@ -8,7 +8,38 @@
|
|
|
8
8
|
[bottomTabsController readyForPresentation];
|
|
9
9
|
for (UIViewController *viewController in bottomTabsController.deselectedViewControllers) {
|
|
10
10
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
11
|
+
UIWindow *preloadWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
|
|
12
|
+
preloadWindow.hidden = NO;
|
|
13
|
+
|
|
14
|
+
dispatch_group_t ready = dispatch_group_create();
|
|
15
|
+
dispatch_group_enter(ready);
|
|
16
|
+
|
|
17
|
+
viewController.waitForRender = YES;
|
|
18
|
+
|
|
19
|
+
[viewController setReactViewReadyCallback:^{
|
|
20
|
+
dispatch_group_leave(ready);
|
|
21
|
+
}];
|
|
22
|
+
|
|
11
23
|
[viewController render];
|
|
24
|
+
|
|
25
|
+
UIView *containerView = nil;
|
|
26
|
+
UIView *reactView = nil;
|
|
27
|
+
if ([viewController isKindOfClass:[UINavigationController class]]) {
|
|
28
|
+
containerView = [(UINavigationController *)viewController topViewController].view;
|
|
29
|
+
reactView = containerView.subviews.firstObject;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (reactView && !reactView.window) {
|
|
33
|
+
[preloadWindow addSubview:reactView];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
dispatch_notify(ready, dispatch_get_main_queue(), ^{
|
|
37
|
+
if (reactView) {
|
|
38
|
+
reactView.frame = containerView.bounds;
|
|
39
|
+
[containerView addSubview:reactView];
|
|
40
|
+
}
|
|
41
|
+
preloadWindow.hidden = YES;
|
|
42
|
+
});
|
|
12
43
|
});
|
|
13
44
|
}
|
|
14
45
|
}];
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
#pragma mark - public
|
|
8
8
|
|
|
9
9
|
- (void)applyBackgroundColor:(UIColor *)backgroundColor translucent:(BOOL)translucent {
|
|
10
|
+
if (@available(iOS 26.0, *)) {
|
|
11
|
+
[self setTabBarTransparentBackground];
|
|
12
|
+
self.tabBar.backgroundColor = UIColor.clearColor;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
10
15
|
if (translucent)
|
|
11
16
|
[self setTabBarTranslucent:YES];
|
|
12
17
|
else if (backgroundColor.isTransparent)
|
|
@@ -19,26 +24,33 @@
|
|
|
19
24
|
|
|
20
25
|
- (void)applyTabBarBorder:(RNNBottomTabsOptions *)options {
|
|
21
26
|
if (options.borderColor.hasValue || options.borderWidth.hasValue) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
UIImage *borderImage = [UIImage
|
|
28
|
+
imageWithSize:CGSizeMake(1.0, [[options.borderWidth withDefault:@(0.1)] floatValue])
|
|
29
|
+
color:[options.borderColor withDefault:UIColor.blackColor]];
|
|
30
|
+
|
|
31
|
+
for (UIViewController *childViewController in self.tabBarController.childViewControllers) {
|
|
32
|
+
childViewController.tabBarItem.standardAppearance.shadowImage = borderImage;
|
|
33
|
+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
|
|
34
|
+
if (@available(iOS 15.0, *)) {
|
|
35
|
+
childViewController.tabBarItem.scrollEdgeAppearance.shadowImage = borderImage;
|
|
36
|
+
}
|
|
37
|
+
#endif
|
|
38
|
+
}
|
|
26
39
|
}
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
- (void)setTabBarBackgroundColor:(UIColor *)backgroundColor {
|
|
30
|
-
[self
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
#endif
|
|
43
|
+
UITabBarAppearance *appearance = [self appearanceWithColor:backgroundColor];
|
|
44
|
+
[self applyTabBarAppearance:appearance];
|
|
45
|
+
self.tabBar.barTintColor = backgroundColor;
|
|
46
|
+
self.tabBar.translucent = NO;
|
|
47
|
+
if (@available(iOS 26.0, *)) {
|
|
48
|
+
self.tabBar.backgroundColor = backgroundColor;
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
- (void)setTabBarTranslucent:(BOOL)translucent {
|
|
53
|
+
self.tabBar.translucent = translucent;
|
|
42
54
|
if (translucent)
|
|
43
55
|
[self setTabBarTranslucentBackground];
|
|
44
56
|
else
|
|
@@ -48,38 +60,63 @@
|
|
|
48
60
|
#pragma mark - private
|
|
49
61
|
|
|
50
62
|
- (void)setTabBarDefaultBackground {
|
|
51
|
-
|
|
63
|
+
if (@available(iOS 26.0, *)) {
|
|
64
|
+
[self setTabBarTransparentBackground];
|
|
65
|
+
self.tabBar.backgroundColor = UIColor.clearColor;
|
|
66
|
+
} else {
|
|
67
|
+
[self setTabBarOpaqueBackground];
|
|
68
|
+
}
|
|
52
69
|
}
|
|
53
70
|
|
|
54
71
|
- (void)setTabBarTranslucentBackground {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
[childViewController.tabBarItem.scrollEdgeAppearance configureWithDefaultBackground];
|
|
60
|
-
}
|
|
61
|
-
#endif
|
|
62
|
-
}
|
|
72
|
+
UITabBarAppearance *appearance = [UITabBarAppearance new];
|
|
73
|
+
[appearance configureWithDefaultBackground];
|
|
74
|
+
[self applyTabBarAppearance:appearance];
|
|
75
|
+
self.tabBar.barTintColor = nil;
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
- (void)setTabBarTransparentBackground {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
#endif
|
|
74
|
-
}
|
|
79
|
+
UITabBarAppearance *appearance = [UITabBarAppearance new];
|
|
80
|
+
[appearance configureWithTransparentBackground];
|
|
81
|
+
appearance.backgroundEffect = nil;
|
|
82
|
+
appearance.backgroundColor = UIColor.clearColor;
|
|
83
|
+
[self applyTabBarAppearance:appearance];
|
|
84
|
+
self.tabBar.barTintColor = UIColor.clearColor;
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
- (void)setTabBarOpaqueBackground {
|
|
88
|
+
UITabBarAppearance *appearance = [self appearanceWithColor:nil];
|
|
89
|
+
[self applyTabBarAppearance:appearance];
|
|
90
|
+
self.tabBar.barTintColor = UIColor.systemBackgroundColor;
|
|
91
|
+
self.tabBar.translucent = NO;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#pragma mark - helpers
|
|
95
|
+
|
|
96
|
+
- (UITabBarAppearance *)appearanceWithColor:(UIColor *)color {
|
|
97
|
+
UITabBarAppearance *appearance = [UITabBarAppearance new];
|
|
98
|
+
[appearance configureWithOpaqueBackground];
|
|
99
|
+
appearance.backgroundEffect = nil;
|
|
100
|
+
appearance.shadowColor = nil;
|
|
101
|
+
UIColor *resolvedColor = color ?: UIColor.systemBackgroundColor;
|
|
102
|
+
appearance.backgroundColor = resolvedColor;
|
|
103
|
+
appearance.backgroundImage = [UIImage imageWithSize:CGSizeMake(1, 1) color:resolvedColor];
|
|
104
|
+
return appearance;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
- (void)applyTabBarAppearance:(UITabBarAppearance *)appearance {
|
|
108
|
+
self.tabBar.standardAppearance = appearance;
|
|
109
|
+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
|
|
110
|
+
if (@available(iOS 15.0, *)) {
|
|
111
|
+
self.tabBar.scrollEdgeAppearance = [appearance copy];
|
|
112
|
+
}
|
|
113
|
+
#endif
|
|
114
|
+
|
|
78
115
|
for (UIViewController *childViewController in self.tabBarController.childViewControllers) {
|
|
79
|
-
|
|
116
|
+
childViewController.tabBarItem.standardAppearance = [appearance copy];
|
|
80
117
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
|
|
81
118
|
if (@available(iOS 15.0, *)) {
|
|
82
|
-
|
|
119
|
+
childViewController.tabBarItem.scrollEdgeAppearance = [appearance copy];
|
|
83
120
|
}
|
|
84
121
|
#endif
|
|
85
122
|
}
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
[bottomTabs setTabBarVisible:[withDefault.bottomTabs.visible withDefault:YES]];
|
|
28
28
|
|
|
29
29
|
[bottomTabs.view setBackgroundColor:[withDefault.layout.backgroundColor withDefault:nil]];
|
|
30
|
+
[bottomTabs setTabBarHideShadow:[withDefault.bottomTabs.hideShadow withDefault:NO]];
|
|
31
|
+
if (options.bottomTabs.barStyle.hasValue) {
|
|
32
|
+
[bottomTabs setTabBarStyle:[RNNConvert UIBarStyle:options.bottomTabs.barStyle.get]];
|
|
33
|
+
}
|
|
30
34
|
[self applyBackgroundColor:[withDefault.bottomTabs.backgroundColor withDefault:nil]
|
|
31
35
|
translucent:[withDefault.bottomTabs.translucent withDefault:NO]];
|
|
32
|
-
[bottomTabs setTabBarHideShadow:[withDefault.bottomTabs.hideShadow withDefault:NO]];
|
|
33
|
-
[bottomTabs setTabBarStyle:[RNNConvert UIBarStyle:[withDefault.bottomTabs.barStyle
|
|
34
|
-
withDefault:@"default"]]];
|
|
35
36
|
[self applyTabBarBorder:withDefault.bottomTabs];
|
|
36
37
|
[self applyTabBarShadow:withDefault.bottomTabs.shadow];
|
|
37
38
|
}
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
if (mergeOptions.bottomTabs.translucent.hasValue) {
|
|
68
|
-
[
|
|
69
|
+
[self setTabBarTranslucent:mergeOptions.bottomTabs.translucent.get];
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
if (mergeOptions.bottomTabs.hideShadow.hasValue) {
|
package/ios/RCTHelpers.mm
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#import "RCTHelpers.h"
|
|
2
2
|
#import <React/RCTFont.h>
|
|
3
|
+
#if __has_include(<React/RCTScrollView.h>)
|
|
3
4
|
#import <React/RCTScrollView.h>
|
|
5
|
+
#endif
|
|
4
6
|
#import <React/RCTView.h>
|
|
5
7
|
|
|
6
8
|
@implementation RCTHelpers
|
|
@@ -201,7 +203,7 @@
|
|
|
201
203
|
UIView *yelloboxParentView = view;
|
|
202
204
|
while (view.superview != nil) {
|
|
203
205
|
yelloboxParentView = yelloboxParentView.superview;
|
|
204
|
-
if ([yelloboxParentView isKindOfClass:
|
|
206
|
+
if ([yelloboxParentView isKindOfClass:NSClassFromString(@"RCTScrollView")]) {
|
|
205
207
|
yelloboxParentView = yelloboxParentView.superview;
|
|
206
208
|
break;
|
|
207
209
|
}
|
package/ios/RNNAppDelegate.mm
CHANGED
|
@@ -5,11 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
#import "RCTAppSetupUtils.h"
|
|
7
7
|
#import <React/CoreModulesPlugins.h>
|
|
8
|
+
#if __has_include(<React/RCTCxxBridgeDelegate.h>)
|
|
8
9
|
#import <React/RCTCxxBridgeDelegate.h>
|
|
10
|
+
#endif
|
|
9
11
|
#import <React/RCTLegacyViewManagerInteropComponentView.h>
|
|
10
12
|
#import <React/RCTSurfacePresenter.h>
|
|
13
|
+
#if __has_include(<React/RCTSurfacePresenterStub.h>)
|
|
11
14
|
#import <React/RCTSurfacePresenterStub.h>
|
|
15
|
+
#endif
|
|
16
|
+
#if __has_include(<React/RCTSurfacePresenterBridgeAdapter.h>)
|
|
12
17
|
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
|
18
|
+
#endif
|
|
13
19
|
#import <ReactCommon/RCTTurboModuleManager.h>
|
|
14
20
|
|
|
15
21
|
#if __has_include(<react/config/ReactNativeConfig.h>)
|
|
@@ -19,9 +25,13 @@
|
|
|
19
25
|
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
|
20
26
|
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
|
21
27
|
#import <React/RCTSurfacePresenter.h>
|
|
28
|
+
#if __has_include(<React/RCTBridge+Private.h>)
|
|
22
29
|
#import <React/RCTBridge+Private.h>
|
|
30
|
+
#endif
|
|
23
31
|
#import <React/RCTImageLoader.h>
|
|
32
|
+
#if __has_include(<React/RCTBridgeProxy.h>)
|
|
24
33
|
#import <React/RCTBridgeProxy.h>
|
|
34
|
+
#endif
|
|
25
35
|
#import <React/RCTSurfacePresenter.h>
|
|
26
36
|
#import <react/utils/ManagedObjectWrapper.h>
|
|
27
37
|
|