Browse Source

Fix bottomTabs visibility (#6230)

* Fix setting root with bottomTabs.visible false
* Fix bottomTabs invisible after hiding through mergeOptions

on iOS, setting root with invisible bottomTabs doesn't work out of the box. We had to change the implementation and load the bottomTabsController children after it has been loaded to the window's rootViewController.
Yogev Ben David 4 years ago
parent
commit
4e1ac71394
No account linked to committer's email address

+ 14
- 0
e2e/SetRoot.test.js View File

@@ -13,4 +13,18 @@ describe('SetRoot', () => {
13 13
     await elementById(TestIDs.SET_MULTIPLE_ROOTS_BTN).tap();
14 14
     await expect(elementById(TestIDs.PUSHED_SCREEN_HEADER)).toBeVisible();
15 15
   });
16
+
17
+  it('set root hides bottomTabs', async () => {
18
+    await elementById(TestIDs.SET_ROOT_HIDES_BOTTOM_TABS_BTN).tap();
19
+    await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeNotVisible();
20
+    await elementById(TestIDs.PUSH_BTN).tap();
21
+    await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeVisible();
22
+  });
23
+
24
+  it('set root with stack hides bottomTabs', async () => {
25
+    await elementById(TestIDs.SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN).tap();
26
+    await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeNotVisible();
27
+    await elementById(TestIDs.POP_BTN).tap();
28
+    await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeVisible();
29
+  });
16 30
 });

+ 1
- 0
lib/ios/BottomTabsBasePresenter.m View File

@@ -17,6 +17,7 @@
17 17
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
18 18
 
19 19
     [bottomTabs setTabBarTestID:[withDefault.bottomTabs.testID getWithDefaultValue:nil]];
20
+    ![withDefault.bottomTabs.visible getWithDefaultValue:YES] ?: [bottomTabs setTabBarVisible:[withDefault.bottomTabs.visible getWithDefaultValue:YES] animated:[withDefault.bottomTabs.animate getWithDefaultValue:NO]];
20 21
     
21 22
     [bottomTabs.view setBackgroundColor:[withDefault.layout.backgroundColor getWithDefaultValue:nil]];
22 23
     [self applyBackgroundColor:[withDefault.bottomTabs.backgroundColor getWithDefaultValue:nil] translucent:[withDefault.bottomTabs.translucent getWithDefaultValue:NO]];

+ 3
- 2
lib/ios/BottomTabsTogetherAttacher.m View File

@@ -1,9 +1,10 @@
1 1
 #import "BottomTabsTogetherAttacher.h"
2
+#import "RNNBottomTabsController.h"
2 3
 
3 4
 @implementation BottomTabsTogetherAttacher
4 5
 
5
-- (void)attach:(UITabBarController *)bottomTabsController {
6
-    for (UIViewController* childViewController in bottomTabsController.childViewControllers) {
6
+- (void)attach:(RNNBottomTabsController *)bottomTabsController {
7
+    for (UIViewController* childViewController in bottomTabsController.pendingChildViewControllers) {
7 8
         [childViewController render];
8 9
     }
9 10
     

+ 2
- 0
lib/ios/RNNBottomTabsController.h View File

@@ -21,4 +21,6 @@
21 21
 
22 22
 - (void)setSelectedIndexByComponentID:(NSString *)componentID;
23 23
 
24
+@property (nonatomic, strong) NSArray* pendingChildViewControllers;
25
+
24 26
 @end

+ 25
- 2
lib/ios/RNNBottomTabsController.m View File

@@ -4,6 +4,7 @@
4 4
 @interface RNNBottomTabsController ()
5 5
 @property (nonatomic, strong) BottomTabPresenter* bottomTabPresenter;
6 6
 @property (nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter;
7
+@property (nonatomic) BOOL viewWillAppearOnce;
7 8
 @end
8 9
 
9 10
 @implementation RNNBottomTabsController {
@@ -25,6 +26,7 @@
25 26
     _bottomTabsAttacher = bottomTabsAttacher;
26 27
     _bottomTabPresenter = bottomTabPresenter;
27 28
     _dotIndicatorPresenter = dotIndicatorPresenter;
29
+    _pendingChildViewControllers = childViewControllers;
28 30
     self = [super initWithLayoutInfo:layoutInfo creator:creator options:options defaultOptions:defaultOptions presenter:presenter eventEmitter:eventEmitter childViewControllers:childViewControllers];
29 31
     if (@available(iOS 13.0, *)) {
30 32
         self.tabBar.standardAppearance = [UITabBarAppearance new];
@@ -32,6 +34,12 @@
32 34
     return self;
33 35
 }
34 36
 
37
+- (void)viewWillAppear:(BOOL)animated {
38
+    [super viewWillAppear:animated];
39
+    _viewWillAppearOnce = YES;
40
+    [self loadChildren:self.pendingChildViewControllers];
41
+}
42
+
35 43
 - (void)onChildAddToParent:(UIViewController *)child options:(RNNNavigationOptions *)options {
36 44
     [_bottomTabPresenter applyOptionsOnWillMoveToParentViewController:options child:child];
37 45
 }
@@ -73,11 +81,14 @@
73 81
 }
74 82
 
75 83
 - (void)setSelectedIndexByComponentID:(NSString *)componentID {
76
-	for (id child in self.childViewControllers) {
84
+    NSArray* children = self.pendingChildViewControllers ?: self.childViewControllers;
85
+	for (id child in children) {
77 86
 		UIViewController<RNNLayoutProtocol>* vc = child;
78 87
 
79 88
 		if ([vc conformsToProtocol:@protocol(RNNLayoutProtocol)] && [vc.layoutInfo.componentId isEqualToString:componentID]) {
80
-			[self setSelectedIndex:[self.childViewControllers indexOfObject:child]];
89
+            NSUInteger selectedIndex = [children indexOfObject:child];
90
+			[self setSelectedIndex:selectedIndex];
91
+            _currentTabIndex = selectedIndex;
81 92
 		}
82 93
 	}
83 94
 }
@@ -87,6 +98,18 @@
87 98
 	[super setSelectedIndex:selectedIndex];
88 99
 }
89 100
 
101
+- (UIViewController *)selectedViewController {
102
+    NSArray* children = self.pendingChildViewControllers ?: self.childViewControllers;
103
+    return children.count ? children[_currentTabIndex] : nil;
104
+}
105
+
106
+- (void)loadChildren:(NSArray *)children {
107
+    if (self.viewWillAppearOnce) {
108
+        [super loadChildren:children];
109
+        self.pendingChildViewControllers = nil;
110
+    }
111
+}
112
+
90 113
 #pragma mark UITabBarControllerDelegate
91 114
 
92 115
 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {

+ 1
- 1
lib/ios/RNNSplitViewController.m View File

@@ -10,7 +10,7 @@
10 10
 }
11 11
 
12 12
 - (UIViewController *)getCurrentChild {
13
-	return self.viewControllers[0];
13
+    return self.viewControllers[0];
14 14
 }
15 15
 
16 16
 # pragma mark - UIViewController overrides

+ 3
- 0
lib/ios/UIViewController+LayoutProtocol.h View File

@@ -42,6 +42,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
42 42
 
43 43
 - (void)screenPopped;
44 44
 
45
+- (void)loadChildren:(NSArray *)children;
46
+
45 47
 @property (nonatomic, retain) RNNBasePresenter* presenter;
46 48
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
47 49
 @property (nonatomic, strong) RNNNavigationOptions* options;
@@ -50,5 +52,6 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
50 52
 @property (nonatomic) id<RNNComponentViewCreator> creator;
51 53
 @property (nonatomic) RNNReactViewReadyCompletionBlock reactViewReadyCallback;
52 54
 @property (nonatomic) BOOL waitForRender;
55
+@property (nonatomic) BOOL isChildViewControllersLoaded;
53 56
 
54 57
 @end

+ 16
- 4
lib/ios/UIViewController+LayoutProtocol.m View File

@@ -12,7 +12,6 @@
12 12
 					  eventEmitter:(RNNEventEmitter *)eventEmitter
13 13
 			  childViewControllers:(NSArray *)childViewControllers {
14 14
 	self = [self init];
15
-	
16 15
 	self.options = options;
17 16
 	self.defaultOptions = defaultOptions;
18 17
 	self.layoutInfo = layoutInfo;
@@ -21,9 +20,7 @@
21 20
     self.presenter = presenter;
22 21
     [self.presenter bindViewController:self];
23 22
     self.extendedLayoutIncludesOpaqueBars = YES;
24
-    if ([self respondsToSelector:@selector(setViewControllers:)]) {
25
-        [self performSelector:@selector(setViewControllers:) withObject:childViewControllers];
26
-    }
23
+    [self loadChildren:childViewControllers];
27 24
     [self.presenter applyOptionsOnInit:self.resolveOptions];
28 25
 
29 26
 	return self;
@@ -74,6 +71,13 @@
74 71
     [self.getCurrentChild render];
75 72
 }
76 73
 
74
+- (void)loadChildren:(NSArray *)children {
75
+    if (!self.isChildViewControllersLoaded && [self respondsToSelector:@selector(setViewControllers:)]) {
76
+        self.isChildViewControllersLoaded = YES;
77
+        [self performSelector:@selector(setViewControllers:) withObject:children];
78
+    }
79
+}
80
+
77 81
 - (void)readyForPresentation {
78 82
     if (self.reactViewReadyCallback) {
79 83
         self.reactViewReadyCallback();
@@ -212,6 +216,14 @@
212 216
 	objc_setAssociatedObject(self, @selector(eventEmitter), eventEmitter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
213 217
 }
214 218
 
219
+- (BOOL)isChildViewControllersLoaded {
220
+    return [objc_getAssociatedObject(self, @selector(isChildViewControllersLoaded)) boolValue];
221
+}
222
+
223
+- (void)setIsChildViewControllersLoaded:(BOOL)isChildViewControllersLoaded {
224
+    objc_setAssociatedObject(self, @selector(isChildViewControllersLoaded), [NSNumber numberWithBool:isChildViewControllersLoaded], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
225
+}
226
+
215 227
 - (id<RNNComponentViewCreator>)creator {
216 228
 	return objc_getAssociatedObject(self, @selector(creator));
217 229
 }

+ 11
- 0
playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m View File

@@ -28,6 +28,7 @@
28 28
 	[[(id)self.uut expect] applyBackgroundColor:nil translucent:NO];
29 29
     [[self.boundViewController expect] setTabBarHideShadow:NO];
30 30
     [[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault];
31
+	[[self.boundViewController expect] setTabBarVisible:YES animated:NO];
31 32
     [self.uut applyOptions:emptyOptions];
32 33
     [self.boundViewController verify];
33 34
 }
@@ -50,6 +51,16 @@
50 51
     [self.boundViewController verify];
51 52
 }
52 53
 
54
+- (void)testApplyOptions_shouldRestoreHiddenTabBar {
55
+    RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
56
+    initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(1)];
57
+	
58
+	[[self.boundViewController expect] setTabBarVisible:YES animated:NO];
59
+
60
+    [self.uut applyOptions:initialOptions];
61
+    [self.boundViewController verify];
62
+}
63
+
53 64
 - (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages {
54 65
 	RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
55 66
 	initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"];

+ 8
- 7
playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m View File

@@ -172,6 +172,7 @@
172 172
 	[self.uut setTabBarItem:item];
173 173
 	[controllers addObject:self.uut];
174 174
 	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
175
+	[vc viewWillAppear:NO];
175 176
 	[self.uut willMoveToParentViewController:vc];
176 177
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
177 178
 
@@ -375,8 +376,8 @@
375 376
 	NSArray* supportedOrientations = @[@"portrait"];
376 377
 	self.options.layout.orientation = supportedOrientations;
377 378
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
378
-    __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
379
-
379
+    __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
380
+	[vc viewWillAppear:NO];
380 381
 	[self.uut viewWillAppear:false];
381 382
 
382 383
 	UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskPortrait;
@@ -387,8 +388,8 @@
387 388
 	NSArray* supportedOrientations = @[@"portrait", @"landscape"];
388 389
 	self.options.layout.orientation = supportedOrientations;
389 390
     NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
390
-    __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
391
-
391
+    __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
392
+	[vc viewWillAppear:NO];
392 393
 	[self.uut viewWillAppear:false];
393 394
 
394 395
 	UIInterfaceOrientationMask expectedOrientation = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape);
@@ -399,8 +400,8 @@
399 400
 	NSArray* supportedOrientations = @[@"all"];
400 401
 	self.options.layout.orientation = supportedOrientations;
401 402
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
402
-	__unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
403
-
403
+	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
404
+	[vc viewWillAppear:NO];
404 405
 	[self.uut viewWillAppear:false];
405 406
 
406 407
 	UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskAll;
@@ -560,7 +561,7 @@
560 561
 
561 562
 - (RNNStackController *)createNavigationController {
562 563
 	RNNStackController* nav = [[RNNStackController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNStackPresenter alloc] init] eventEmitter:nil childViewControllers:@[self.uut]];
563
-	
564
+	[nav viewWillAppear:NO];
564 565
 	return nav;
565 566
 }
566 567
 

+ 13
- 22
playground/ios/NavigationTests/BottomTabsControllerTest.m View File

@@ -42,7 +42,8 @@
42 42
     UIViewController *vc1 = [[UIViewController alloc] init];
43 43
     UIViewController *vc2 = [[UIViewController alloc] init];
44 44
 
45
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNComponentPresenter alloc] init] eventEmitter:nil childViewControllers:@[vc1, vc2]];
45
+	RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[vc1, vc2]];
46
+	[uut viewWillAppear:YES];
46 47
     XCTAssertTrue(uut.viewControllers.count == 2);
47 48
 }
48 49
 
@@ -51,32 +52,19 @@
51 52
     RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
52 53
     RNNBottomTabsPresenter *presenter = [[RNNBottomTabsPresenter alloc] init];
53 54
     NSArray *childViewControllers = @[[UIViewController new]];
54
-
55
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:childViewControllers];
56
-    XCTAssertTrue(uut.layoutInfo == layoutInfo);
57
-    XCTAssertTrue(uut.options == options);
58
-    XCTAssertTrue(uut.presenter == presenter);
59
-    XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
60
-}
61
-
62
-- (void)testInitWithEventEmmiter_shouldInitializeDependencies {
63
-    RNNLayoutInfo *layoutInfo = [RNNLayoutInfo new];
64
-    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
65
-    RNNBottomTabsPresenter *presenter = [[RNNBottomTabsPresenter alloc] init];
66
-    RNNEventEmitter *eventEmmiter = [RNNEventEmitter new];
67
-
68
-    NSArray *childViewControllers = @[[UIViewController new]];
69
-
70
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:eventEmmiter childViewControllers:childViewControllers];
55
+	RNNEventEmitter *eventEmmiter = [RNNEventEmitter new];
56
+	
57
+    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter bottomTabPresenter:nil dotIndicatorPresenter:nil eventEmitter:eventEmmiter childViewControllers:childViewControllers bottomTabsAttacher:nil];
58
+	[uut viewWillAppear:YES];
71 59
     XCTAssertTrue(uut.layoutInfo == layoutInfo);
72 60
     XCTAssertTrue(uut.options == options);
73 61
     XCTAssertTrue(uut.presenter == presenter);
74 62
     XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
75
-    XCTAssertTrue(uut.eventEmitter == eventEmmiter);
63
+	XCTAssertTrue(uut.eventEmitter == eventEmmiter);
76 64
 }
77 65
 
78 66
 - (void)testInitWithLayoutInfo_shouldSetDelegate {
79
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNBasePresenter alloc] init] eventEmitter:nil childViewControllers:nil];
67
+    RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[]];
80 68
 
81 69
     XCTAssertTrue(uut.delegate == uut);
82 70
 }
@@ -179,7 +167,9 @@
179 167
 
180 168
     RNNComponentViewController *vc = [[RNNComponentViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
181 169
 
182
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:[RNNBottomTabsPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
170
+    RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[[UIViewController new], vc]];
171
+	[uut viewWillAppear:YES];
172
+	
183 173
     [uut setSelectedIndexByComponentID:@"componentId"];
184 174
     XCTAssertTrue(uut.selectedIndex == 1);
185 175
 }
@@ -189,7 +179,8 @@
189 179
     options.bottomTabs.currentTabIndex = [[IntNumber alloc] initWithValue:@(1)];
190 180
 
191 181
     RNNComponentViewController *vc = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
192
-    RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:nil presenter:[RNNBottomTabsPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
182
+	RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[[UIViewController new], vc] options:options];
183
+	[uut viewWillAppear:YES];
193 184
 
194 185
     XCTAssertTrue(uut.selectedIndex == 1);
195 186
 }

+ 13
- 1
playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m View File

@@ -26,6 +26,7 @@
26 26
 	self.dotIndicatorPresenter = [OCMockObject partialMockForObject:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:nil]];
27 27
     self.uut = [OCMockObject partialMockForObject:[BottomTabsPresenterCreator createWithDefaultOptions:nil]];
28 28
 	self.boundViewController = [OCMockObject partialMockForObject:[[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:self.uut bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:nil] dotIndicatorPresenter:self.dotIndicatorPresenter eventEmitter:nil childViewControllers:self.children bottomTabsAttacher:nil]];
29
+	[self.boundViewController viewWillAppear:YES];
29 30
     [self.uut bindViewController:self.boundViewController];
30 31
     self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
31 32
 }
@@ -35,7 +36,8 @@
35 36
     [[self.boundViewController expect] setTabBarTestID:nil];
36 37
     [[(id)self.uut expect] applyBackgroundColor:nil translucent:NO];
37 38
     [[self.boundViewController expect] setTabBarHideShadow:NO];
38
-    [[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault];
39
+    [[self.boundViewController expect] setTabBarVisible:YES animated:NO];
40
+	[[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault];
39 41
     [self.uut applyOptions:emptyOptions];
40 42
     [self.boundViewController verify];
41 43
 }
@@ -58,6 +60,16 @@
58 60
     [self.boundViewController verify];
59 61
 }
60 62
 
63
+- (void)testApplyOptions_shouldRestoreHiddenTabBar {
64
+    RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
65
+    initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(1)];
66
+	
67
+	[[self.boundViewController expect] setTabBarVisible:YES animated:NO];
68
+
69
+    [self.uut applyOptions:initialOptions];
70
+    [self.boundViewController verify];
71
+}
72
+
61 73
 - (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages {
62 74
 	RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
63 75
 	initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"];

+ 2
- 0
playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h View File

@@ -7,4 +7,6 @@
7 7
 
8 8
 + (RNNBottomTabsController *)createWithChildren:(NSArray *)children;
9 9
 
10
++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children options:(RNNNavigationOptions *)options;
11
+
10 12
 @end

+ 5
- 2
playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m View File

@@ -5,13 +5,16 @@
5 5
 @implementation RNNBottomTabsController (Helpers)
6 6
 
7 7
 + (RNNBottomTabsController *)create {
8
-	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
9 8
 	return [self createWithChildren:nil];
10 9
 }
11 10
 
12 11
 + (RNNBottomTabsController *)createWithChildren:(NSArray *)children {
12
+	return [self createWithChildren:children options:[[RNNNavigationOptions alloc] initEmptyOptions]];
13
+}
14
+
15
++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children options:(RNNNavigationOptions *)options {
13 16
 	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
14
-	return [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:defaultOptions presenter:[BottomTabsPresenterCreator createWithDefaultOptions:defaultOptions] bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:defaultOptions] dotIndicatorPresenter:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:defaultOptions] eventEmitter:nil childViewControllers:children bottomTabsAttacher:nil];
17
+	return [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:defaultOptions presenter:[BottomTabsPresenterCreator createWithDefaultOptions:defaultOptions] bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:defaultOptions] dotIndicatorPresenter:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:defaultOptions] eventEmitter:nil childViewControllers:children bottomTabsAttacher:nil];
15 18
 }
16 19
 
17 20
 @end

+ 5
- 2
playground/ios/NavigationTests/RNNCommandsHandlerTest.m View File

@@ -382,7 +382,7 @@
382 382
 	
383 383
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
384 384
 	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] bottomTabPresenter:nil dotIndicatorPresenter:nil eventEmitter:_eventEmmiter childViewControllers:@[_vc1, _vc2] bottomTabsAttacher:attacher];
385
-
385
+	[tabBarController viewWillAppear:YES];
386 386
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
387 387
 	
388 388
 	[self.uut setRoot:@{} commandId:@"" completion:^{}];
@@ -400,7 +400,8 @@
400 400
 
401 401
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
402 402
 	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] bottomTabPresenter:nil dotIndicatorPresenter:nil eventEmitter:_eventEmmiter childViewControllers:@[_vc1, _vc2] bottomTabsAttacher:attacher];
403
-
403
+	[tabBarController viewWillAppear:YES];
404
+	
404 405
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
405 406
 
406 407
 	[self.uut setRoot:@{} commandId:@"" completion:^{
@@ -424,6 +425,7 @@
424 425
 	RNNComponentViewController* secondChild = [RNNComponentViewController createWithComponentId:@"second" initialOptions:secondChildOptions];
425 426
 	
426 427
 	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[RNNNavigationOptions emptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:[RNNNavigationOptions emptyOptions]] dotIndicatorPresenter:nil eventEmitter:_eventEmmiter childViewControllers:@[firstChild, secondChild] bottomTabsAttacher:nil];
428
+	[tabBarController viewWillAppear:YES];
427 429
 	
428 430
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
429 431
 	[self.mainWindow setRootViewController:tabBarController];
@@ -455,6 +457,7 @@
455 457
 	RNNComponentViewController* secondChild = [RNNComponentViewController createWithComponentId:@"second" initialOptions:secondChildOptions];
456 458
 	
457 459
 	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[RNNNavigationOptions emptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:[RNNNavigationOptions emptyOptions]] dotIndicatorPresenter:nil eventEmitter:_eventEmmiter childViewControllers:@[stack, secondChild] bottomTabsAttacher:nil];
460
+	[tabBarController viewWillAppear:YES];
458 461
 	
459 462
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
460 463
 	[self.mainWindow setRootViewController:tabBarController];

+ 1
- 1
playground/ios/NavigationTests/RNNControllerFactoryTest.m View File

@@ -115,7 +115,7 @@
115 115
 												 @"data": @{},
116 116
 												 @"children": @[]}]}]};
117 117
 	RNNBottomTabsController* tabBar = (RNNBottomTabsController*) [self.factory createLayout:layout];
118
-	
118
+	[tabBar viewWillAppear:YES];
119 119
 	XCTAssertTrue([tabBar isMemberOfClass:[RNNBottomTabsController class]]);
120 120
 	XCTAssertTrue(tabBar.childViewControllers.count == 1);
121 121
 	XCTAssertTrue([tabBar.childViewControllers[0] isMemberOfClass:[RNNStackController class]]);

+ 8
- 8
playground/ios/NavigationTests/RNNRootViewControllerTest.m View File

@@ -182,6 +182,7 @@
182 182
 	NSMutableArray* controllers = [NSMutableArray new];
183 183
 	[controllers addObject:self.uut];
184 184
 	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
185
+	[vc viewWillAppear:NO];
185 186
 	[self.uut willMoveToParentViewController:vc];
186 187
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
187 188
 
@@ -369,9 +370,8 @@
369 370
 	self.options.layout.orientation = supportedOrientations;
370 371
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
371 372
     __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
372
-
373
-	[self.uut viewWillAppear:false];
374
-
373
+	[vc viewWillAppear:NO];
374
+	[self.uut viewWillAppear:NO];
375 375
 	UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskPortrait;
376 376
 	XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation);
377 377
 }
@@ -381,8 +381,8 @@
381 381
 	self.options.layout.orientation = supportedOrientations;
382 382
     NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
383 383
     __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
384
-
385
-	[self.uut viewWillAppear:false];
384
+	[vc viewWillAppear:NO];
385
+	[self.uut viewWillAppear:NO];
386 386
 
387 387
 	UIInterfaceOrientationMask expectedOrientation = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape);
388 388
 	XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation);
@@ -393,8 +393,8 @@
393 393
 	self.options.layout.orientation = supportedOrientations;
394 394
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
395 395
 	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
396
-
397
-	[self.uut viewWillAppear:false];
396
+	[vc viewWillAppear:NO];
397
+	[self.uut viewWillAppear:NO];
398 398
 
399 399
 	UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskAll;
400 400
 	XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation);
@@ -553,7 +553,7 @@
553 553
 
554 554
 - (RNNStackController *)createNavigationController {
555 555
 	RNNStackController* nav = [[RNNStackController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNStackPresenter alloc] init] eventEmitter:nil childViewControllers:@[self.uut]];
556
-	
556
+
557 557
 	return nav;
558 558
 }
559 559
 

+ 18
- 0
playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m View File

@@ -37,6 +37,24 @@
37 37
 	NSArray* childViewControllers = @[child1, child2];
38 38
 	UINavigationController* uut = [[UINavigationController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers];
39 39
 	
40
+	XCTAssertEqual(uut.viewControllers.count, 2);
41
+}
42
+
43
+- (void)testInitBottomTabsWithLayoutInfoShouldNotSetChildViewControllers {
44
+	UIViewController* child1 = [UIViewController new];
45
+	UIViewController* child2 = [UIViewController new];
46
+	NSArray* childViewControllers = @[child1, child2];
47
+	RNNBottomTabsController* uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers];
48
+	
49
+	XCTAssertEqual(uut.viewControllers.count, 0);
50
+}
51
+
52
+- (void)testLoadChildrenShouldSetChildViewControllers {
53
+	UIViewController* child1 = [UIViewController new];
54
+	UIViewController* child2 = [UIViewController new];
55
+	NSArray* childViewControllers = @[child1, child2];
56
+	UINavigationController* uut = [[UINavigationController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers];
57
+	[uut loadChildren:childViewControllers];
40 58
 	XCTAssertEqual(uut.viewControllers[0], child1);
41 59
 	XCTAssertEqual(uut.viewControllers[1], child2);
42 60
 }

+ 68
- 1
playground/src/screens/SetRootScreen.js View File

@@ -5,7 +5,10 @@ const Navigation = require('./../services/Navigation');
5 5
 const {
6 6
   NAVIGATION_TAB,
7 7
   SET_MULTIPLE_ROOTS_BTN,
8
-  SET_ROOT_BTN
8
+  SET_ROOT_BTN,
9
+  LAYOUTS_TAB,
10
+  SET_ROOT_HIDES_BOTTOM_TABS_BTN,
11
+  SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN
9 12
 } = require('../testIDs');
10 13
 const Screens = require('./Screens');
11 14
 const { logLifecycleEvent } = require('./StaticLifecycleOverlay');
@@ -37,6 +40,8 @@ class SetRootScreen  extends React.Component {
37 40
       <Root componentId={this.props.componentId}>
38 41
         <Button label='Set Root' testID={SET_ROOT_BTN} onPress={this.setSingleRoot} />
39 42
         <Button label='Set Multiple Roots' testID={SET_MULTIPLE_ROOTS_BTN} onPress={this.setMultipleRoot} />
43
+        <Button label='Set Root - hides bottomTabs' testID={SET_ROOT_HIDES_BOTTOM_TABS_BTN} onPress={this.setRootHidesBottomTabs} />
44
+        <Button label='Set Root with stack - hides bottomTabs' testID={SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN} onPress={this.setRootWithStackHidesBottomTabs} />
40 45
       </Root>
41 46
     );
42 47
   }
@@ -68,6 +73,68 @@ class SetRootScreen  extends React.Component {
68 73
       }
69 74
     }
70 75
   });
76
+  
77
+  setRootHidesBottomTabs = async () => await Navigation.setRoot({
78
+    root: {
79
+      bottomTabs: {
80
+        children: [{
81
+          stack: {
82
+            id: 'stack',
83
+            children: [{
84
+              component: {
85
+                id: 'component',
86
+                name: Screens.Pushed,
87
+                options: {
88
+                  bottomTabs: {
89
+                    visible: false
90
+                  }
91
+                }
92
+              }
93
+            }]
94
+          }
95
+        }],
96
+        options: {
97
+          bottomTabs: {
98
+            testID: LAYOUTS_TAB
99
+          }
100
+        }
101
+      }
102
+    }
103
+  });
104
+
105
+  setRootWithStackHidesBottomTabs = async () => await Navigation.setRoot({
106
+    root: {
107
+      bottomTabs: {
108
+        children: [{
109
+          stack: {
110
+            id: 'stack',
111
+            children: [{
112
+              component: {
113
+                id: 'component',
114
+                name: Screens.Pushed
115
+              }
116
+            },
117
+            {
118
+              component: {
119
+                id: 'component2',
120
+                name: Screens.Pushed,
121
+                options: {
122
+                  bottomTabs: {
123
+                    visible: false
124
+                  }
125
+                }
126
+              }
127
+            }]
128
+          }
129
+        }],
130
+        options: {
131
+          bottomTabs: {
132
+            testID: LAYOUTS_TAB
133
+          }
134
+        }
135
+      }
136
+    }
137
+  });
71 138
 }
72 139
 
73 140
 module.exports = SetRootScreen;

+ 2
- 0
playground/src/testIDs.js View File

@@ -69,6 +69,8 @@ module.exports = {
69 69
   PUSH_TO_TEST_DID_DISAPPEAR_BTN: 'PUSH_TO_TEST_DID_DISAPPEAR_BTN',
70 70
   SET_ROOT_BTN: 'SET_ROOT_BTN',
71 71
   SET_MULTIPLE_ROOTS_BTN: 'SET_MULTIPLE_ROOTS_BTN',
72
+  SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN: 'SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN',
73
+  SET_ROOT_HIDES_BOTTOM_TABS_BTN: 'SET_ROOT_HIDES_BOTTOM_TABS_BTN',
72 74
   ADD_BACK_HANDLER: 'ADD_BACK_HANDLER',
73 75
   REMOVE_BACK_HANDLER: 'REMOVE_BACK_HANDLER',
74 76
   OPEN_LEFT_SIDE_MENU_BTN: 'OPEN_LEFT_SIDE_MENU_BTN',