react-native-navigation 8.8.2 → 8.8.3-snapshot.2513
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/src/main/java/com/reactnativenavigation/hierarchy/root/RootAnimator.kt +5 -2
- package/android/src/main/java/com/reactnativenavigation/react/DevBundleDownloadListenerAdapter.java +5 -8
- package/android/src/main/java/com/reactnativenavigation/react/JsDevReloadHandlerFacade.java +5 -8
- package/android/src/main/java/com/reactnativenavigation/react/NavigationTurboModule.kt +5 -0
- package/android/src/main/java/com/reactnativenavigation/react/ReloadHandlerFacade.java +5 -8
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java +2 -1
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt +4 -1
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +3 -1
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/RootPresenter.java +11 -1
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java +1 -1
- package/android/src/test/java/com/reactnativenavigation/BaseRobolectricTest.kt +1 -1
- package/android/src/test/java/com/reactnativenavigation/BaseTest.kt +3 -2
- package/android/src/test/java/com/reactnativenavigation/ShadowReactView.java +45 -0
- package/android/src/test/java/com/reactnativenavigation/ShadowSoLoader.java +28 -0
- package/android/src/test/java/com/reactnativenavigation/TestApplication.kt +2 -2
- package/android/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java +14 -7
- package/android/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/utils/ButtonPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/utils/LayoutFactoryTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManagerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/FloatingActionButtonTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TitleBarButtonControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TitleBarReactViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarButtonControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/views/TitleSubTitleLayoutTest.kt +0 -1
- package/ios/BottomTabPresenter.h +2 -0
- package/ios/BottomTabPresenter.mm +1 -3
- package/ios/BottomTabsAfterInitialTabAttacher.mm +4 -0
- package/ios/BottomTabsTogetherAttacher.mm +4 -0
- package/ios/RNNAppDelegate.mm +15 -4
- package/ios/RNNBackButtonOptions.h +1 -0
- package/ios/RNNBackButtonOptions.mm +3 -0
- package/ios/RNNBottomTabOptions.h +1 -0
- package/ios/RNNBottomTabOptions.mm +4 -1
- package/ios/RNNBottomTabsController.mm +48 -8
- package/ios/RNNButtonOptions.h +1 -0
- package/ios/RNNButtonOptions.mm +4 -0
- package/ios/RNNComponentOptions.mm +1 -1
- package/ios/RNNComponentPresenter.mm +85 -0
- package/ios/RNNComponentViewController.mm +5 -0
- package/ios/RNNNavigationOptions.h +2 -0
- package/ios/RNNNavigationOptions.mm +4 -0
- package/ios/RNNScreenTransition.mm +1 -1
- package/ios/RNNScrollEdgeEffectOptions.h +20 -0
- package/ios/RNNScrollEdgeEffectOptions.mm +45 -0
- package/ios/RNNStackController.mm +76 -1
- package/ios/RNNTabBarItemCreator.h +2 -0
- package/ios/RNNTabBarItemCreator.mm +98 -0
- package/ios/RNNUIBarButtonItem.mm +26 -0
- package/ios/StackControllerDelegate.mm +1 -0
- package/ios/TopBarPresenter.mm +26 -2
- package/ios/TransitionOptions.mm +2 -2
- package/ios/UIViewController+LayoutProtocol.h +2 -0
- package/lib/module/interfaces/Options.js.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +95 -0
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/interfaces/Options.ts +109 -0
|
@@ -40,7 +40,6 @@ import static org.mockito.Mockito.spy;
|
|
|
40
40
|
import static org.mockito.Mockito.times;
|
|
41
41
|
import static org.mockito.Mockito.verify;
|
|
42
42
|
|
|
43
|
-
@Ignore("New architecture - WIP")
|
|
44
43
|
public class TopTabsViewControllerTest extends BaseTest {
|
|
45
44
|
private static final int SIZE = 2;
|
|
46
45
|
|
|
@@ -40,7 +40,6 @@ import static org.mockito.Mockito.times;
|
|
|
40
40
|
import static org.mockito.Mockito.verify;
|
|
41
41
|
import static org.mockito.Mockito.withSettings;
|
|
42
42
|
|
|
43
|
-
@Ignore("New architecture - WIP")
|
|
44
43
|
public class ViewControllerTest extends BaseTest {
|
|
45
44
|
|
|
46
45
|
private ViewController uut;
|
package/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt
CHANGED
|
@@ -30,7 +30,6 @@ import kotlin.test.assertFalse
|
|
|
30
30
|
private const val UUT_WIDTH = 1000
|
|
31
31
|
private const val UUT_HEIGHT = 100
|
|
32
32
|
|
|
33
|
-
@Ignore("New architecture - failed to fix")
|
|
34
33
|
class TitleAndButtonsContainerTest : BaseTest() {
|
|
35
34
|
lateinit var uut: TitleAndButtonsContainer
|
|
36
35
|
private lateinit var activity: Activity
|
package/ios/BottomTabPresenter.h
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#import "RNNTabBarItemCreator.h"
|
|
3
3
|
@interface BottomTabPresenter : RNNBasePresenter
|
|
4
4
|
|
|
5
|
+
@property(nonatomic, strong, readonly) RNNTabBarItemCreator *tabCreator;
|
|
6
|
+
|
|
5
7
|
- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions
|
|
6
8
|
tabCreator:(RNNTabBarItemCreator *)tabCreator;
|
|
7
9
|
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
#import "UIViewController+LayoutProtocol.h"
|
|
4
4
|
#import "UIViewController+RNNOptions.h"
|
|
5
5
|
|
|
6
|
-
@implementation BottomTabPresenter
|
|
7
|
-
RNNTabBarItemCreator *_tabCreator;
|
|
8
|
-
}
|
|
6
|
+
@implementation BottomTabPresenter
|
|
9
7
|
|
|
10
8
|
- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions
|
|
11
9
|
tabCreator:(RNNTabBarItemCreator *)tabCreator {
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
for (UIViewController *viewController in bottomTabsController.deselectedViewControllers) {
|
|
10
10
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
11
11
|
UIWindow *preloadWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
|
|
12
|
+
// Clip the preload window so the deselected tab's full-screen reactView,
|
|
13
|
+
// which is briefly hosted here while it renders, can't draw outside the
|
|
14
|
+
// zero-frame window and flicker over the already-visible selected tab.
|
|
15
|
+
preloadWindow.clipsToBounds = YES;
|
|
12
16
|
preloadWindow.hidden = NO;
|
|
13
17
|
|
|
14
18
|
dispatch_group_t ready = dispatch_group_create();
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
dispatch_group_t ready = dispatch_group_create();
|
|
8
8
|
|
|
9
9
|
UIWindow *preloadWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
|
|
10
|
+
// Clip the preload window so the full-screen reactViews hosted here while
|
|
11
|
+
// they render can't draw outside the zero-frame window. Today the splash
|
|
12
|
+
// screen masks this, but without clipping it's a latent flicker.
|
|
13
|
+
preloadWindow.clipsToBounds = YES;
|
|
10
14
|
preloadWindow.hidden = NO;
|
|
11
15
|
|
|
12
16
|
NSMapTable *reactViewToParent = [NSMapTable strongToStrongObjectsMapTable];
|
package/ios/RNNAppDelegate.mm
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
#import <react/featureflags/ReactNativeFeatureFlagsDefaults.h>
|
|
5
5
|
|
|
6
6
|
#import "RCTAppSetupUtils.h"
|
|
7
|
-
#import <React/CoreModulesPlugins.h>
|
|
8
7
|
#if __has_include(<React/RCTCxxBridgeDelegate.h>)
|
|
9
8
|
#import <React/RCTCxxBridgeDelegate.h>
|
|
10
9
|
#endif
|
|
@@ -56,12 +55,17 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
|
56
55
|
- (BOOL)application:(UIApplication *)application
|
|
57
56
|
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
58
57
|
|
|
58
|
+
// RN 0.77 & RN0.78
|
|
59
59
|
#if !RNN_RN_VERSION_79_OR_NEWER
|
|
60
|
-
|
|
60
|
+
#if __has_include(<ReactAppDependencyProvider/RCTAppDependencyProvider.h>)
|
|
61
|
+
self.dependencyProvider = [RCTAppDependencyProvider new];
|
|
62
|
+
#endif
|
|
63
|
+
// RN 0.77
|
|
61
64
|
#if !RNN_RN_VERSION_78
|
|
65
|
+
[self _setUpFeatureFlags];
|
|
62
66
|
self.rootViewFactory = [self createRCTRootViewFactory];
|
|
63
|
-
#else
|
|
64
|
-
self.reactNativeFactory = [[RCTReactNativeFactory alloc]
|
|
67
|
+
#else // RN0.78
|
|
68
|
+
self.reactNativeFactory = [[RCTReactNativeFactory alloc] initWithDelegate:self];
|
|
65
69
|
self.reactNativeFactory.rootViewFactory = [self createRCTRootViewFactory];
|
|
66
70
|
#endif
|
|
67
71
|
|
|
@@ -119,6 +123,13 @@ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
|
119
123
|
return [[RCTRootViewFactory alloc] initWithConfiguration:configuration andTurboModuleManagerDelegate:self];
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
#pragma mark - RCTTurboModuleManagerDelegate
|
|
127
|
+
|
|
128
|
+
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
|
129
|
+
{
|
|
130
|
+
return RCTAppSetupDefaultModuleFromClass(moduleClass, self.dependencyProvider);
|
|
131
|
+
}
|
|
132
|
+
|
|
122
133
|
#pragma mark - Feature Flags
|
|
123
134
|
class RCTAppDelegateBridgelessFeatureFlags : public facebook::react::ReactNativeFeatureFlagsDefaults {
|
|
124
135
|
public:
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
@property(nonatomic, strong) Text *identifier;
|
|
19
19
|
@property(nonatomic, strong) Bool *popStackOnPress;
|
|
20
20
|
@property(nonatomic, strong) RNNIconBackgroundOptions *iconBackground;
|
|
21
|
+
@property(nonatomic, strong) Bool *hideSharedBackground;
|
|
21
22
|
|
|
22
23
|
- (BOOL)hasValue;
|
|
23
24
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
self.displayMode = [TextParser parse:dict key:@"displayMode"];
|
|
21
21
|
self.popStackOnPress = [BoolParser parse:dict key:@"popStackOnPress"];
|
|
22
22
|
self.iconBackground = [[RNNIconBackgroundOptions alloc] initWithDict:dict[@"iconBackground"] enabled:nil];
|
|
23
|
+
self.hideSharedBackground = [BoolParser parse:dict key:@"hideSharedBackground"];
|
|
23
24
|
|
|
24
25
|
return self;
|
|
25
26
|
}
|
|
@@ -54,6 +55,8 @@
|
|
|
54
55
|
self.popStackOnPress = options.popStackOnPress;
|
|
55
56
|
if (options.sfSymbol.hasValue)
|
|
56
57
|
self.sfSymbol = options.sfSymbol;
|
|
58
|
+
if (options.hideSharedBackground.hasValue)
|
|
59
|
+
self.hideSharedBackground = options.hideSharedBackground;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
- (BOOL)hasValue {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
self.selectTabOnPress = [BoolParser parse:dict key:@"selectTabOnPress"];
|
|
32
32
|
self.sfSymbol = [TextParser parse:dict key:@"sfSymbol"];
|
|
33
33
|
self.sfSelectedSymbol = [TextParser parse:dict key:@"sfSelectedSymbol"];
|
|
34
|
+
self.role = [TextParser parse:dict key:@"role"];
|
|
34
35
|
|
|
35
36
|
return self;
|
|
36
37
|
}
|
|
@@ -76,6 +77,8 @@
|
|
|
76
77
|
self.sfSymbol = options.sfSymbol;
|
|
77
78
|
if (options.sfSelectedSymbol.hasValue)
|
|
78
79
|
self.sfSelectedSymbol = options.sfSelectedSymbol;
|
|
80
|
+
if (options.role.hasValue)
|
|
81
|
+
self.role = options.role;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
- (BOOL)hasValue {
|
|
@@ -85,7 +88,7 @@
|
|
|
85
88
|
self.iconColor.hasValue || self.selectedIconColor.hasValue ||
|
|
86
89
|
self.selectedTextColor.hasValue || self.iconInsets.hasValue || self.textColor.hasValue ||
|
|
87
90
|
self.visible.hasValue || self.selectTabOnPress.hasValue || self.sfSymbol.hasValue ||
|
|
88
|
-
self.sfSelectedSymbol.hasValue;
|
|
91
|
+
self.sfSelectedSymbol.hasValue || self.role.hasValue;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
@end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "RNNBottomTabsController.h"
|
|
2
|
+
#import "RNNTabBarItemCreator.h"
|
|
2
3
|
#import "UITabBarController+RNNOptions.h"
|
|
3
4
|
#import "UITabBarController+RNNUtils.h"
|
|
4
5
|
|
|
@@ -7,6 +8,8 @@
|
|
|
7
8
|
@property(nonatomic, strong) RNNDotIndicatorPresenter *dotIndicatorPresenter;
|
|
8
9
|
@property(nonatomic, strong) UILongPressGestureRecognizer *longPressRecognizer;
|
|
9
10
|
|
|
11
|
+
- (void)rnn_cycleAllTabsThenRestoreInitialSelection;
|
|
12
|
+
|
|
10
13
|
@end
|
|
11
14
|
|
|
12
15
|
@implementation RNNBottomTabsController {
|
|
@@ -16,6 +19,8 @@
|
|
|
16
19
|
BOOL _tabBarNeedsRestore;
|
|
17
20
|
RNNNavigationOptions *_options;
|
|
18
21
|
BOOL _didFinishSetup;
|
|
22
|
+
BOOL _rnnDidApplyInitialTabBarSelectionFix;
|
|
23
|
+
BOOL _rnnSuppressTabSelectionEvents;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
|
|
@@ -49,12 +54,7 @@
|
|
|
49
54
|
eventEmitter:eventEmitter
|
|
50
55
|
childViewControllers:childViewControllers];
|
|
51
56
|
|
|
52
|
-
if (@available(iOS
|
|
53
|
-
UITabBarAppearance *appearance = [UITabBarAppearance new];
|
|
54
|
-
[appearance configureWithDefaultBackground];
|
|
55
|
-
self.tabBar.standardAppearance = appearance;
|
|
56
|
-
self.tabBar.scrollEdgeAppearance = [appearance copy];
|
|
57
|
-
} else if (@available(iOS 13.0, *)) {
|
|
57
|
+
if (@available(iOS 13.0, *)) {
|
|
58
58
|
UITabBarAppearance *appearance = [UITabBarAppearance new];
|
|
59
59
|
[appearance configureWithOpaqueBackground];
|
|
60
60
|
appearance.backgroundEffect = nil;
|
|
@@ -90,11 +90,44 @@
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
- (void)
|
|
94
|
-
[super
|
|
93
|
+
- (void)viewDidAppear:(BOOL)animated {
|
|
94
|
+
[super viewDidAppear:animated];
|
|
95
|
+
// iOS 26: first layout can misplace tab item titles; cycling selection (then restoring) forces a
|
|
96
|
+
// correct layout without user interaction. Defer so all tab children are in the hierarchy.
|
|
97
|
+
if (@available(iOS 26.0, *)) {
|
|
98
|
+
if (!_rnnDidApplyInitialTabBarSelectionFix) {
|
|
99
|
+
_rnnDidApplyInitialTabBarSelectionFix = YES;
|
|
100
|
+
__weak RNNBottomTabsController *weakSelf = self;
|
|
101
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
102
|
+
[weakSelf rnn_cycleAllTabsThenRestoreInitialSelection];
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)rnn_cycleAllTabsThenRestoreInitialSelection {
|
|
109
|
+
NSUInteger count = self.childViewControllers.count;
|
|
110
|
+
if (count <= 1) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
NSUInteger initial = _currentTabIndex;
|
|
115
|
+
if (initial >= count) {
|
|
116
|
+
initial = 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_rnnSuppressTabSelectionEvents = YES;
|
|
120
|
+
[UIView performWithoutAnimation:^{
|
|
121
|
+
for (NSUInteger i = 0; i < count; i++) {
|
|
122
|
+
[self setSelectedIndex:i];
|
|
123
|
+
}
|
|
124
|
+
[self setSelectedIndex:initial];
|
|
125
|
+
}];
|
|
126
|
+
_rnnSuppressTabSelectionEvents = NO;
|
|
95
127
|
}
|
|
96
128
|
|
|
97
129
|
- (void)createTabBarItems:(NSArray<UIViewController *> *)childViewControllers {
|
|
130
|
+
_bottomTabPresenter.tabCreator.searchRoleUsed = NO;
|
|
98
131
|
for (UIViewController *child in childViewControllers) {
|
|
99
132
|
[_bottomTabPresenter applyOptions:child.resolveOptions child:child];
|
|
100
133
|
}
|
|
@@ -200,6 +233,9 @@
|
|
|
200
233
|
|
|
201
234
|
- (void)tabBarController:(UITabBarController *)tabBarController
|
|
202
235
|
didSelectViewController:(UIViewController *)viewController {
|
|
236
|
+
if (_rnnSuppressTabSelectionEvents) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
203
239
|
[self.eventEmitter sendBottomTabSelected:@(tabBarController.selectedIndex)
|
|
204
240
|
unselected:@(_previousTabIndex)];
|
|
205
241
|
}
|
|
@@ -216,6 +252,10 @@
|
|
|
216
252
|
NSUInteger _index = [tabBarController.viewControllers indexOfObject:viewController];
|
|
217
253
|
BOOL isMoreTab = ![tabBarController.viewControllers containsObject:viewController];
|
|
218
254
|
|
|
255
|
+
if (_rnnSuppressTabSelectionEvents) {
|
|
256
|
+
return YES;
|
|
257
|
+
}
|
|
258
|
+
|
|
219
259
|
[self.eventEmitter sendBottomTabPressed:@(_index)];
|
|
220
260
|
|
|
221
261
|
if ([[viewController resolveOptions].bottomTab.selectTabOnPress withDefault:YES] || isMoreTab) {
|
package/ios/RNNButtonOptions.h
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
@property(nonatomic, strong) RNNComponentOptions *component;
|
|
24
24
|
@property(nonatomic, strong) RNNIconBackgroundOptions *iconBackground;
|
|
25
25
|
@property(nonatomic, strong) Bool *disableIconTint;
|
|
26
|
+
@property(nonatomic, strong) Bool *hideSharedBackground;
|
|
26
27
|
|
|
27
28
|
- (RNNButtonOptions *)withDefault:(RNNButtonOptions *)defaultOptions;
|
|
28
29
|
|
package/ios/RNNButtonOptions.mm
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
enabled:self.enabled];
|
|
26
26
|
self.systemItem = [TextParser parse:dict key:@"systemItem"];
|
|
27
27
|
self.disableIconTint = [BoolParser parse:dict key:@"disableIconTint"];
|
|
28
|
+
self.hideSharedBackground = [BoolParser parse:dict key:@"hideSharedBackground"];
|
|
28
29
|
|
|
29
30
|
return self;
|
|
30
31
|
}
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
newOptions.iconBackground = self.iconBackground.copy;
|
|
50
51
|
newOptions.systemItem = self.systemItem.copy;
|
|
51
52
|
newOptions.disableIconTint = self.disableIconTint.copy;
|
|
53
|
+
newOptions.hideSharedBackground = self.hideSharedBackground.copy;
|
|
52
54
|
return newOptions;
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -88,6 +90,8 @@
|
|
|
88
90
|
self.systemItem = options.systemItem;
|
|
89
91
|
if (options.disableIconTint.hasValue)
|
|
90
92
|
self.disableIconTint = options.disableIconTint;
|
|
93
|
+
if (options.hideSharedBackground.hasValue)
|
|
94
|
+
self.hideSharedBackground = options.hideSharedBackground;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
- (BOOL)shouldCreateCustomView {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
self.name = [TextParser parse:dict key:@"name"];
|
|
10
10
|
self.componentId = [TextParser parse:dict key:@"componentId"];
|
|
11
11
|
self.alignment = [TextParser parse:dict key:@"alignment"];
|
|
12
|
-
|
|
12
|
+
self.waitForRender = [BoolParser parse:dict key:@"waitForRender"];
|
|
13
13
|
|
|
14
14
|
return self;
|
|
15
15
|
}
|
|
@@ -1,9 +1,83 @@
|
|
|
1
1
|
#import "RNNComponentPresenter.h"
|
|
2
2
|
#import "RNNComponentViewController.h"
|
|
3
|
+
#import "RNNScrollEdgeEffectOptions.h"
|
|
3
4
|
#import "TopBarTitlePresenter.h"
|
|
4
5
|
#import "UITabBarController+RNNOptions.h"
|
|
5
6
|
#import "UIViewController+RNNOptions.h"
|
|
6
7
|
|
|
8
|
+
static NSNumber *RNNScrollEdgeEffectStyleFromString(NSString *value) {
|
|
9
|
+
if ([value isEqualToString:@"hard"])
|
|
10
|
+
return @(2);
|
|
11
|
+
if ([value isEqualToString:@"soft"])
|
|
12
|
+
return @(1);
|
|
13
|
+
return @(0); // automatic
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static RNNScrollEdgeOptions *RNNScrollEdgeOptionsForKey(RNNScrollEdgeEffectOptions *options,
|
|
17
|
+
NSString *edgeKey) {
|
|
18
|
+
if ([edgeKey isEqualToString:@"topEdgeEffect"])
|
|
19
|
+
return options.top;
|
|
20
|
+
if ([edgeKey isEqualToString:@"bottomEdgeEffect"])
|
|
21
|
+
return options.bottom;
|
|
22
|
+
if ([edgeKey isEqualToString:@"leftEdgeEffect"])
|
|
23
|
+
return options.left;
|
|
24
|
+
if ([edgeKey isEqualToString:@"rightEdgeEffect"])
|
|
25
|
+
return options.right;
|
|
26
|
+
return nil;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static BOOL RNNScrollEdgeEffectHasAnyValue(RNNScrollEdgeEffectOptions *options) {
|
|
30
|
+
if (!options)
|
|
31
|
+
return NO;
|
|
32
|
+
if (options.hidden.hasValue || options.style.hasValue)
|
|
33
|
+
return YES;
|
|
34
|
+
NSArray<RNNScrollEdgeOptions *> *edges =
|
|
35
|
+
@[ options.top ?: [RNNScrollEdgeOptions new], options.bottom ?: [RNNScrollEdgeOptions new],
|
|
36
|
+
options.left ?: [RNNScrollEdgeOptions new], options.right ?: [RNNScrollEdgeOptions new] ];
|
|
37
|
+
for (RNNScrollEdgeOptions *edge in edges) {
|
|
38
|
+
if (edge.hidden.hasValue || edge.style.hasValue)
|
|
39
|
+
return YES;
|
|
40
|
+
}
|
|
41
|
+
return NO;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void RNNApplyScrollEdgeEffectToScrollView(UIScrollView *scrollView,
|
|
45
|
+
RNNScrollEdgeEffectOptions *options) {
|
|
46
|
+
if (![scrollView respondsToSelector:NSSelectorFromString(@"topEdgeEffect")])
|
|
47
|
+
return;
|
|
48
|
+
NSArray<NSString *> *edgeKeys =
|
|
49
|
+
@[ @"topEdgeEffect", @"bottomEdgeEffect", @"leftEdgeEffect", @"rightEdgeEffect" ];
|
|
50
|
+
for (NSString *key in edgeKeys) {
|
|
51
|
+
id effect = [scrollView valueForKey:key];
|
|
52
|
+
if (!effect)
|
|
53
|
+
continue;
|
|
54
|
+
RNNScrollEdgeOptions *perEdge = RNNScrollEdgeOptionsForKey(options, key);
|
|
55
|
+
|
|
56
|
+
Bool *hidden = (perEdge.hidden.hasValue) ? perEdge.hidden : options.hidden;
|
|
57
|
+
Text *style = (perEdge.style.hasValue) ? perEdge.style : options.style;
|
|
58
|
+
|
|
59
|
+
if (hidden.hasValue &&
|
|
60
|
+
[effect respondsToSelector:NSSelectorFromString(@"setHidden:")]) {
|
|
61
|
+
[effect setValue:@([hidden withDefault:NO]) forKey:@"hidden"];
|
|
62
|
+
}
|
|
63
|
+
if (style.hasValue &&
|
|
64
|
+
[effect respondsToSelector:NSSelectorFromString(@"setStyle:")]) {
|
|
65
|
+
[effect setValue:RNNScrollEdgeEffectStyleFromString(style.get) forKey:@"style"];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static void RNNApplyScrollEdgeEffectToView(UIView *view, RNNScrollEdgeEffectOptions *options) {
|
|
71
|
+
if (!RNNScrollEdgeEffectHasAnyValue(options))
|
|
72
|
+
return;
|
|
73
|
+
if ([view isKindOfClass:[UIScrollView class]]) {
|
|
74
|
+
RNNApplyScrollEdgeEffectToScrollView((UIScrollView *)view, options);
|
|
75
|
+
}
|
|
76
|
+
for (UIView *subview in view.subviews) {
|
|
77
|
+
RNNApplyScrollEdgeEffectToView(subview, options);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
7
81
|
@implementation RNNComponentPresenter {
|
|
8
82
|
TopBarTitlePresenter *_topBarTitlePresenter;
|
|
9
83
|
RNNButtonsPresenter *_buttonsPresenter;
|
|
@@ -34,6 +108,11 @@
|
|
|
34
108
|
- (void)componentDidAppear {
|
|
35
109
|
[_topBarTitlePresenter componentDidAppear];
|
|
36
110
|
[_buttonsPresenter componentDidAppear];
|
|
111
|
+
|
|
112
|
+
RNNComponentViewController *viewController = self.boundViewController;
|
|
113
|
+
RNNNavigationOptions *withDefault =
|
|
114
|
+
[viewController.options withDefault:[self defaultOptions]];
|
|
115
|
+
RNNApplyScrollEdgeEffectToView(viewController.view, withDefault.scrollEdgeEffect);
|
|
37
116
|
}
|
|
38
117
|
|
|
39
118
|
- (void)componentDidDisappear {
|
|
@@ -102,6 +181,8 @@
|
|
|
102
181
|
defaultDisabledColor:withDefault.topBar.rightButtonDisabledColor
|
|
103
182
|
animated:[withDefault.topBar.animateRightButtons withDefault:NO]];
|
|
104
183
|
}
|
|
184
|
+
|
|
185
|
+
RNNApplyScrollEdgeEffectToView(viewController.view, withDefault.scrollEdgeEffect);
|
|
105
186
|
}
|
|
106
187
|
|
|
107
188
|
- (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
|
|
@@ -236,6 +317,10 @@
|
|
|
236
317
|
}
|
|
237
318
|
|
|
238
319
|
[_topBarTitlePresenter mergeOptions:mergeOptions.topBar resolvedOptions:withDefault.topBar];
|
|
320
|
+
|
|
321
|
+
if (RNNScrollEdgeEffectHasAnyValue(mergeOptions.scrollEdgeEffect)) {
|
|
322
|
+
RNNApplyScrollEdgeEffectToView(viewController.view, mergeOptions.scrollEdgeEffect);
|
|
323
|
+
}
|
|
239
324
|
}
|
|
240
325
|
|
|
241
326
|
- (void)renderComponents:(RNNNavigationOptions *)options
|
|
@@ -155,6 +155,11 @@
|
|
|
155
155
|
isFocused:searchController.searchBar.isFirstResponder];
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
- (void)destroyReactView {
|
|
159
|
+
[self.reactView removeFromSuperview];
|
|
160
|
+
self.reactView = nil;
|
|
161
|
+
}
|
|
162
|
+
|
|
158
163
|
- (void)screenPopped {
|
|
159
164
|
[_eventEmitter sendScreenPoppedEvent:self.layoutInfo.componentId];
|
|
160
165
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#import "RNNModalOptions.h"
|
|
7
7
|
#import "RNNOverlayOptions.h"
|
|
8
8
|
#import "RNNPreviewOptions.h"
|
|
9
|
+
#import "RNNScrollEdgeEffectOptions.h"
|
|
9
10
|
#import "RNNSharedElementAnimationOptions.h"
|
|
10
11
|
#import "RNNSideMenuOptions.h"
|
|
11
12
|
#import "RNNSplitViewOptions.h"
|
|
@@ -34,6 +35,7 @@ extern const NSInteger BLUR_TOPBAR_TAG;
|
|
|
34
35
|
@property(nonatomic, strong) RNNModalOptions *modal;
|
|
35
36
|
@property(nonatomic, strong) DeprecationOptions *deprecations;
|
|
36
37
|
@property(nonatomic, strong) WindowOptions *window;
|
|
38
|
+
@property(nonatomic, strong) RNNScrollEdgeEffectOptions *scrollEdgeEffect;
|
|
37
39
|
|
|
38
40
|
@property(nonatomic, strong) Bool *popGesture;
|
|
39
41
|
@property(nonatomic, strong) Bool *navigationButtonEventOnSwipeBack;
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
self.modal = [[RNNModalOptions alloc] initWithDict:dict[@"modal"]];
|
|
40
40
|
self.deprecations = [[DeprecationOptions alloc] initWithDict:dict[@"deprecations"]];
|
|
41
41
|
self.window = [[WindowOptions alloc] initWithDict:dict[@"window"]];
|
|
42
|
+
self.scrollEdgeEffect =
|
|
43
|
+
[[RNNScrollEdgeEffectOptions alloc] initWithDict:dict[@"scrollEdgeEffect"]];
|
|
42
44
|
|
|
43
45
|
self.popGesture = [[Bool alloc] initWithValue:dict[@"popGesture"]];
|
|
44
46
|
self.navigationButtonEventOnSwipeBack = [[Bool alloc] initWithValue:dict[@"navigationButtonEventOnSwipeBack"]];
|
|
@@ -71,6 +73,7 @@
|
|
|
71
73
|
[result.modal mergeOptions:options.modal];
|
|
72
74
|
[result.deprecations mergeOptions:options.deprecations];
|
|
73
75
|
[result.window mergeOptions:options.window];
|
|
76
|
+
[result.scrollEdgeEffect mergeOptions:options.scrollEdgeEffect];
|
|
74
77
|
|
|
75
78
|
if (options.popGesture.hasValue)
|
|
76
79
|
result.popGesture = options.popGesture;
|
|
@@ -105,6 +108,7 @@
|
|
|
105
108
|
[newOptions.modal mergeOptions:self.modal];
|
|
106
109
|
[newOptions.deprecations mergeOptions:self.deprecations];
|
|
107
110
|
[newOptions.window mergeOptions:self.window];
|
|
111
|
+
[newOptions.scrollEdgeEffect mergeOptions:self.scrollEdgeEffect];
|
|
108
112
|
|
|
109
113
|
newOptions.popGesture = self.popGesture;
|
|
110
114
|
newOptions.navigationButtonEventOnSwipeBack = self.navigationButtonEventOnSwipeBack;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
self.content = [[RNNEnterExitAnimation alloc] initWithDict:dict[@"content"]];
|
|
12
12
|
self.bottomTabs = [[ElementTransitionOptions alloc] initWithDict:dict[@"bottomTabs"]];
|
|
13
13
|
self.enable = [BoolParser parse:dict key:@"enabled"];
|
|
14
|
-
|
|
14
|
+
self.waitForRender = [BoolParser parse:dict key:@"waitForRender"];
|
|
15
15
|
self.duration = [TimeIntervalParser parse:dict key:@"duration"];
|
|
16
16
|
self.sharedElementTransitions = [OptionsArrayParser parse:dict
|
|
17
17
|
key:@"sharedElementTransitions"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#import "RNNOptions.h"
|
|
2
|
+
|
|
3
|
+
@interface RNNScrollEdgeOptions : RNNOptions
|
|
4
|
+
|
|
5
|
+
@property(nonatomic, strong) Bool *hidden;
|
|
6
|
+
@property(nonatomic, strong) Text *style;
|
|
7
|
+
|
|
8
|
+
@end
|
|
9
|
+
|
|
10
|
+
@interface RNNScrollEdgeEffectOptions : RNNOptions
|
|
11
|
+
|
|
12
|
+
@property(nonatomic, strong) Bool *hidden;
|
|
13
|
+
@property(nonatomic, strong) Text *style;
|
|
14
|
+
|
|
15
|
+
@property(nonatomic, strong) RNNScrollEdgeOptions *top;
|
|
16
|
+
@property(nonatomic, strong) RNNScrollEdgeOptions *bottom;
|
|
17
|
+
@property(nonatomic, strong) RNNScrollEdgeOptions *left;
|
|
18
|
+
@property(nonatomic, strong) RNNScrollEdgeOptions *right;
|
|
19
|
+
|
|
20
|
+
@end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#import "RNNScrollEdgeEffectOptions.h"
|
|
2
|
+
|
|
3
|
+
@implementation RNNScrollEdgeOptions
|
|
4
|
+
|
|
5
|
+
- (instancetype)initWithDict:(NSDictionary *)dict {
|
|
6
|
+
self = [super initWithDict:dict];
|
|
7
|
+
self.hidden = [BoolParser parse:dict key:@"hidden"];
|
|
8
|
+
self.style = [TextParser parse:dict key:@"style"];
|
|
9
|
+
return self;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
- (void)mergeOptions:(RNNScrollEdgeOptions *)options {
|
|
13
|
+
if (options.hidden.hasValue)
|
|
14
|
+
self.hidden = options.hidden;
|
|
15
|
+
if (options.style.hasValue)
|
|
16
|
+
self.style = options.style;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
@implementation RNNScrollEdgeEffectOptions
|
|
22
|
+
|
|
23
|
+
- (instancetype)initWithDict:(NSDictionary *)dict {
|
|
24
|
+
self = [super initWithDict:dict];
|
|
25
|
+
self.hidden = [BoolParser parse:dict key:@"hidden"];
|
|
26
|
+
self.style = [TextParser parse:dict key:@"style"];
|
|
27
|
+
self.top = [[RNNScrollEdgeOptions alloc] initWithDict:dict[@"top"]];
|
|
28
|
+
self.bottom = [[RNNScrollEdgeOptions alloc] initWithDict:dict[@"bottom"]];
|
|
29
|
+
self.left = [[RNNScrollEdgeOptions alloc] initWithDict:dict[@"left"]];
|
|
30
|
+
self.right = [[RNNScrollEdgeOptions alloc] initWithDict:dict[@"right"]];
|
|
31
|
+
return self;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (void)mergeOptions:(RNNScrollEdgeEffectOptions *)options {
|
|
35
|
+
if (options.hidden.hasValue)
|
|
36
|
+
self.hidden = options.hidden;
|
|
37
|
+
if (options.style.hasValue)
|
|
38
|
+
self.style = options.style;
|
|
39
|
+
[self.top mergeOptions:options.top];
|
|
40
|
+
[self.bottom mergeOptions:options.bottom];
|
|
41
|
+
[self.left mergeOptions:options.left];
|
|
42
|
+
[self.right mergeOptions:options.right];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@end
|
|
@@ -42,7 +42,82 @@
|
|
|
42
42
|
|
|
43
43
|
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
|
|
44
44
|
[self prepareForPop];
|
|
45
|
-
|
|
45
|
+
UIViewController *previousTop = self.topViewController;
|
|
46
|
+
UIView *snapshot = [self snapshotTopView:animated];
|
|
47
|
+
|
|
48
|
+
UIViewController *poppedVC = [super popViewControllerAnimated:animated];
|
|
49
|
+
if (!poppedVC) {
|
|
50
|
+
[snapshot removeFromSuperview];
|
|
51
|
+
return nil;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
id<UIViewControllerTransitionCoordinator> coordinator = self.transitionCoordinator;
|
|
55
|
+
if (coordinator && coordinator.isInteractive) {
|
|
56
|
+
// Interactive pop (swipe-back): remove snapshot overlay — UIKit shows the live
|
|
57
|
+
// view during the gesture. Skip early teardown so the React view stays alive
|
|
58
|
+
// if the gesture is cancelled. The delegate's didShowViewController handles
|
|
59
|
+
// cleanup once the animation finishes.
|
|
60
|
+
[snapshot removeFromSuperview];
|
|
61
|
+
} else {
|
|
62
|
+
[self teardownPoppedControllers:@[ poppedVC ] previousTop:previousTop];
|
|
63
|
+
}
|
|
64
|
+
return poppedVC;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController
|
|
68
|
+
animated:(BOOL)animated {
|
|
69
|
+
UIViewController *previousTop = self.topViewController;
|
|
70
|
+
UIView *snapshot = [self snapshotTopView:animated];
|
|
71
|
+
|
|
72
|
+
NSArray<UIViewController *> *poppedVCs =
|
|
73
|
+
[super popToViewController:viewController animated:animated];
|
|
74
|
+
if (poppedVCs.count > 0) {
|
|
75
|
+
[self teardownPoppedControllers:poppedVCs previousTop:previousTop];
|
|
76
|
+
} else {
|
|
77
|
+
[snapshot removeFromSuperview];
|
|
78
|
+
}
|
|
79
|
+
return poppedVCs;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated {
|
|
83
|
+
UIViewController *previousTop = self.topViewController;
|
|
84
|
+
UIView *snapshot = [self snapshotTopView:animated];
|
|
85
|
+
|
|
86
|
+
NSArray<UIViewController *> *poppedVCs =
|
|
87
|
+
[super popToRootViewControllerAnimated:animated];
|
|
88
|
+
if (poppedVCs.count > 0) {
|
|
89
|
+
[self teardownPoppedControllers:poppedVCs previousTop:previousTop];
|
|
90
|
+
} else {
|
|
91
|
+
[snapshot removeFromSuperview];
|
|
92
|
+
}
|
|
93
|
+
return poppedVCs;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#pragma mark - React view teardown
|
|
97
|
+
|
|
98
|
+
- (UIView *)snapshotTopView:(BOOL)animated {
|
|
99
|
+
if (!animated) return nil;
|
|
100
|
+
UIViewController *topVC = self.topViewController;
|
|
101
|
+
if (!topVC.isViewLoaded || !topVC.view.window) return nil;
|
|
102
|
+
|
|
103
|
+
UIView *snapshot = [topVC.view snapshotViewAfterScreenUpdates:NO];
|
|
104
|
+
if (snapshot) {
|
|
105
|
+
snapshot.frame = topVC.view.bounds;
|
|
106
|
+
snapshot.autoresizingMask =
|
|
107
|
+
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
108
|
+
[topVC.view addSubview:snapshot];
|
|
109
|
+
}
|
|
110
|
+
return snapshot;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
- (void)teardownPoppedControllers:(NSArray<UIViewController *> *)poppedVCs
|
|
114
|
+
previousTop:(UIViewController *)previousTop {
|
|
115
|
+
for (UIViewController *vc in poppedVCs) {
|
|
116
|
+
if (vc == previousTop && [vc isKindOfClass:[RNNComponentViewController class]]) {
|
|
117
|
+
[[(RNNComponentViewController *)vc reactView] componentDidDisappear];
|
|
118
|
+
}
|
|
119
|
+
[vc destroyReactView];
|
|
120
|
+
}
|
|
46
121
|
}
|
|
47
122
|
|
|
48
123
|
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
|