From 4e1ac713944373d8d2c5b251912e1a872b2d00a7 Mon Sep 17 00:00:00 2001 From: Yogev Ben David Date: Tue, 26 May 2020 11:45:00 +0300 Subject: [PATCH] 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. --- e2e/SetRoot.test.js | 14 ++++ lib/ios/BottomTabsBasePresenter.m | 1 + lib/ios/BottomTabsTogetherAttacher.m | 5 +- lib/ios/RNNBottomTabsController.h | 2 + lib/ios/RNNBottomTabsController.m | 27 +++++++- lib/ios/RNNSplitViewController.m | 2 +- lib/ios/UIViewController+LayoutProtocol.h | 3 + lib/ios/UIViewController+LayoutProtocol.m | 20 ++++-- .../RNNBottomTabsPresenterTest.m | 11 +++ .../RNNRootViewControllerTest.m | 15 ++-- .../BottomTabsControllerTest.m | 35 ++++------ .../RNNBottomTabsAppearancePresenterTest.m | 14 +++- .../RNNBottomTabsController+Helpers.h | 2 + .../RNNBottomTabsController+Helpers.m | 7 +- .../NavigationTests/RNNCommandsHandlerTest.m | 7 +- .../RNNControllerFactoryTest.m | 2 +- .../RNNRootViewControllerTest.m | 16 ++--- .../UIViewController+LayoutProtocolTest.m | 18 +++++ playground/src/screens/SetRootScreen.js | 69 ++++++++++++++++++- playground/src/testIDs.js | 2 + 20 files changed, 219 insertions(+), 53 deletions(-) diff --git a/e2e/SetRoot.test.js b/e2e/SetRoot.test.js index e5b9192ab0e..c224cc39ec9 100644 --- a/e2e/SetRoot.test.js +++ b/e2e/SetRoot.test.js @@ -13,4 +13,18 @@ describe('SetRoot', () => { await elementById(TestIDs.SET_MULTIPLE_ROOTS_BTN).tap(); await expect(elementById(TestIDs.PUSHED_SCREEN_HEADER)).toBeVisible(); }); + + it('set root hides bottomTabs', async () => { + await elementById(TestIDs.SET_ROOT_HIDES_BOTTOM_TABS_BTN).tap(); + await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeNotVisible(); + await elementById(TestIDs.PUSH_BTN).tap(); + await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeVisible(); + }); + + it('set root with stack hides bottomTabs', async () => { + await elementById(TestIDs.SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN).tap(); + await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeNotVisible(); + await elementById(TestIDs.POP_BTN).tap(); + await expect(elementById(TestIDs.LAYOUTS_TAB)).toBeVisible(); + }); }); diff --git a/lib/ios/BottomTabsBasePresenter.m b/lib/ios/BottomTabsBasePresenter.m index 118e14da164..2b5dfbbd07b 100644 --- a/lib/ios/BottomTabsBasePresenter.m +++ b/lib/ios/BottomTabsBasePresenter.m @@ -17,6 +17,7 @@ - (void)applyOptions:(RNNNavigationOptions *)options { RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]]; [bottomTabs setTabBarTestID:[withDefault.bottomTabs.testID getWithDefaultValue:nil]]; + ![withDefault.bottomTabs.visible getWithDefaultValue:YES] ?: [bottomTabs setTabBarVisible:[withDefault.bottomTabs.visible getWithDefaultValue:YES] animated:[withDefault.bottomTabs.animate getWithDefaultValue:NO]]; [bottomTabs.view setBackgroundColor:[withDefault.layout.backgroundColor getWithDefaultValue:nil]]; [self applyBackgroundColor:[withDefault.bottomTabs.backgroundColor getWithDefaultValue:nil] translucent:[withDefault.bottomTabs.translucent getWithDefaultValue:NO]]; diff --git a/lib/ios/BottomTabsTogetherAttacher.m b/lib/ios/BottomTabsTogetherAttacher.m index 85264fb6e47..d94ac24d71e 100644 --- a/lib/ios/BottomTabsTogetherAttacher.m +++ b/lib/ios/BottomTabsTogetherAttacher.m @@ -1,9 +1,10 @@ #import "BottomTabsTogetherAttacher.h" +#import "RNNBottomTabsController.h" @implementation BottomTabsTogetherAttacher -- (void)attach:(UITabBarController *)bottomTabsController { - for (UIViewController* childViewController in bottomTabsController.childViewControllers) { +- (void)attach:(RNNBottomTabsController *)bottomTabsController { + for (UIViewController* childViewController in bottomTabsController.pendingChildViewControllers) { [childViewController render]; } diff --git a/lib/ios/RNNBottomTabsController.h b/lib/ios/RNNBottomTabsController.h index 2ee79c745cf..52d5dbb4469 100644 --- a/lib/ios/RNNBottomTabsController.h +++ b/lib/ios/RNNBottomTabsController.h @@ -21,4 +21,6 @@ - (void)setSelectedIndexByComponentID:(NSString *)componentID; +@property (nonatomic, strong) NSArray* pendingChildViewControllers; + @end diff --git a/lib/ios/RNNBottomTabsController.m b/lib/ios/RNNBottomTabsController.m index 455fb187cff..54419b51c7a 100644 --- a/lib/ios/RNNBottomTabsController.m +++ b/lib/ios/RNNBottomTabsController.m @@ -4,6 +4,7 @@ @interface RNNBottomTabsController () @property (nonatomic, strong) BottomTabPresenter* bottomTabPresenter; @property (nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter; +@property (nonatomic) BOOL viewWillAppearOnce; @end @implementation RNNBottomTabsController { @@ -25,6 +26,7 @@ - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo _bottomTabsAttacher = bottomTabsAttacher; _bottomTabPresenter = bottomTabPresenter; _dotIndicatorPresenter = dotIndicatorPresenter; + _pendingChildViewControllers = childViewControllers; self = [super initWithLayoutInfo:layoutInfo creator:creator options:options defaultOptions:defaultOptions presenter:presenter eventEmitter:eventEmitter childViewControllers:childViewControllers]; if (@available(iOS 13.0, *)) { self.tabBar.standardAppearance = [UITabBarAppearance new]; @@ -32,6 +34,12 @@ - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo return self; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + _viewWillAppearOnce = YES; + [self loadChildren:self.pendingChildViewControllers]; +} + - (void)onChildAddToParent:(UIViewController *)child options:(RNNNavigationOptions *)options { [_bottomTabPresenter applyOptionsOnWillMoveToParentViewController:options child:child]; } @@ -73,11 +81,14 @@ - (CGFloat)getBottomTabsHeight { } - (void)setSelectedIndexByComponentID:(NSString *)componentID { - for (id child in self.childViewControllers) { + NSArray* children = self.pendingChildViewControllers ?: self.childViewControllers; + for (id child in children) { UIViewController* vc = child; if ([vc conformsToProtocol:@protocol(RNNLayoutProtocol)] && [vc.layoutInfo.componentId isEqualToString:componentID]) { - [self setSelectedIndex:[self.childViewControllers indexOfObject:child]]; + NSUInteger selectedIndex = [children indexOfObject:child]; + [self setSelectedIndex:selectedIndex]; + _currentTabIndex = selectedIndex; } } } @@ -87,6 +98,18 @@ - (void)setSelectedIndex:(NSUInteger)selectedIndex { [super setSelectedIndex:selectedIndex]; } +- (UIViewController *)selectedViewController { + NSArray* children = self.pendingChildViewControllers ?: self.childViewControllers; + return children.count ? children[_currentTabIndex] : nil; +} + +- (void)loadChildren:(NSArray *)children { + if (self.viewWillAppearOnce) { + [super loadChildren:children]; + self.pendingChildViewControllers = nil; + } +} + #pragma mark UITabBarControllerDelegate - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController { diff --git a/lib/ios/RNNSplitViewController.m b/lib/ios/RNNSplitViewController.m index 98035729405..dbbb1d49921 100644 --- a/lib/ios/RNNSplitViewController.m +++ b/lib/ios/RNNSplitViewController.m @@ -10,7 +10,7 @@ - (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControlle } - (UIViewController *)getCurrentChild { - return self.viewControllers[0]; + return self.viewControllers[0]; } # pragma mark - UIViewController overrides diff --git a/lib/ios/UIViewController+LayoutProtocol.h b/lib/ios/UIViewController+LayoutProtocol.h index 9efc78ffbc1..0f69a8feafb 100644 --- a/lib/ios/UIViewController+LayoutProtocol.h +++ b/lib/ios/UIViewController+LayoutProtocol.h @@ -42,6 +42,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void); - (void)screenPopped; +- (void)loadChildren:(NSArray *)children; + @property (nonatomic, retain) RNNBasePresenter* presenter; @property (nonatomic, retain) RNNLayoutInfo* layoutInfo; @property (nonatomic, strong) RNNNavigationOptions* options; @@ -50,5 +52,6 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void); @property (nonatomic) id creator; @property (nonatomic) RNNReactViewReadyCompletionBlock reactViewReadyCallback; @property (nonatomic) BOOL waitForRender; +@property (nonatomic) BOOL isChildViewControllersLoaded; @end diff --git a/lib/ios/UIViewController+LayoutProtocol.m b/lib/ios/UIViewController+LayoutProtocol.m index 02fb3eac910..804b8a93abd 100644 --- a/lib/ios/UIViewController+LayoutProtocol.m +++ b/lib/ios/UIViewController+LayoutProtocol.m @@ -12,7 +12,6 @@ - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo eventEmitter:(RNNEventEmitter *)eventEmitter childViewControllers:(NSArray *)childViewControllers { self = [self init]; - self.options = options; self.defaultOptions = defaultOptions; self.layoutInfo = layoutInfo; @@ -21,9 +20,7 @@ - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo self.presenter = presenter; [self.presenter bindViewController:self]; self.extendedLayoutIncludesOpaqueBars = YES; - if ([self respondsToSelector:@selector(setViewControllers:)]) { - [self performSelector:@selector(setViewControllers:) withObject:childViewControllers]; - } + [self loadChildren:childViewControllers]; [self.presenter applyOptionsOnInit:self.resolveOptions]; return self; @@ -74,6 +71,13 @@ - (void)render { [self.getCurrentChild render]; } +- (void)loadChildren:(NSArray *)children { + if (!self.isChildViewControllersLoaded && [self respondsToSelector:@selector(setViewControllers:)]) { + self.isChildViewControllersLoaded = YES; + [self performSelector:@selector(setViewControllers:) withObject:children]; + } +} + - (void)readyForPresentation { if (self.reactViewReadyCallback) { self.reactViewReadyCallback(); @@ -212,6 +216,14 @@ - (void)setEventEmitter:(RNNEventEmitter *)eventEmitter { objc_setAssociatedObject(self, @selector(eventEmitter), eventEmitter, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } +- (BOOL)isChildViewControllersLoaded { + return [objc_getAssociatedObject(self, @selector(isChildViewControllersLoaded)) boolValue]; +} + +- (void)setIsChildViewControllersLoaded:(BOOL)isChildViewControllersLoaded { + objc_setAssociatedObject(self, @selector(isChildViewControllersLoaded), [NSNumber numberWithBool:isChildViewControllersLoaded], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + - (id)creator { return objc_getAssociatedObject(self, @selector(creator)); } diff --git a/playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m b/playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m index 35aa121337a..86d73182809 100644 --- a/playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m +++ b/playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m @@ -28,6 +28,7 @@ - (void)testApplyOptions_shouldSetDefaultEmptyOptions { [[(id)self.uut expect] applyBackgroundColor:nil translucent:NO]; [[self.boundViewController expect] setTabBarHideShadow:NO]; [[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault]; + [[self.boundViewController expect] setTabBarVisible:YES animated:NO]; [self.uut applyOptions:emptyOptions]; [self.boundViewController verify]; } @@ -50,6 +51,16 @@ - (void)testApplyOptions_shouldApplyOptions { [self.boundViewController verify]; } +- (void)testApplyOptions_shouldRestoreHiddenTabBar { + RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; + initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(1)]; + + [[self.boundViewController expect] setTabBarVisible:YES animated:NO]; + + [self.uut applyOptions:initialOptions]; + [self.boundViewController verify]; +} + - (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages { RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"]; diff --git a/playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m b/playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m index c4e24f49032..91c6f5f167a 100644 --- a/playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m +++ b/playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m @@ -172,6 +172,7 @@ -(void)testTabBadge { [self.uut setTabBarItem:item]; [controllers addObject:self.uut]; __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; + [vc viewWillAppear:NO]; [self.uut willMoveToParentViewController:vc]; XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]); @@ -375,8 +376,8 @@ -(void)testOrientationTabsController_portrait { NSArray* supportedOrientations = @[@"portrait"]; self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; - __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers]; - + __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; + [vc viewWillAppear:NO]; [self.uut viewWillAppear:false]; UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskPortrait; @@ -387,8 +388,8 @@ -(void)testOrientationTabsController_portraitAndLandscape { NSArray* supportedOrientations = @[@"portrait", @"landscape"]; self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; - __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers]; - + __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; + [vc viewWillAppear:NO]; [self.uut viewWillAppear:false]; UIInterfaceOrientationMask expectedOrientation = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape); @@ -399,8 +400,8 @@ -(void)testOrientationTabsController_all { NSArray* supportedOrientations = @[@"all"]; self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; - __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers]; - + __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; + [vc viewWillAppear:NO]; [self.uut viewWillAppear:false]; UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskAll; @@ -560,7 +561,7 @@ - (void)testOverrideOptions { - (RNNStackController *)createNavigationController { RNNStackController* nav = [[RNNStackController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNStackPresenter alloc] init] eventEmitter:nil childViewControllers:@[self.uut]]; - + [nav viewWillAppear:NO]; return nav; } diff --git a/playground/ios/NavigationTests/BottomTabsControllerTest.m b/playground/ios/NavigationTests/BottomTabsControllerTest.m index 9948450f0ee..d1889243b2b 100644 --- a/playground/ios/NavigationTests/BottomTabsControllerTest.m +++ b/playground/ios/NavigationTests/BottomTabsControllerTest.m @@ -42,7 +42,8 @@ - (void)testInitWithLayoutInfo_shouldSetMultipleViewControllers { UIViewController *vc1 = [[UIViewController alloc] init]; UIViewController *vc2 = [[UIViewController alloc] init]; - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNComponentPresenter alloc] init] eventEmitter:nil childViewControllers:@[vc1, vc2]]; + RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[vc1, vc2]]; + [uut viewWillAppear:YES]; XCTAssertTrue(uut.viewControllers.count == 2); } @@ -51,32 +52,19 @@ - (void)testInitWithLayoutInfo_shouldInitializeDependencies { RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}]; RNNBottomTabsPresenter *presenter = [[RNNBottomTabsPresenter alloc] init]; NSArray *childViewControllers = @[[UIViewController new]]; - - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:childViewControllers]; - XCTAssertTrue(uut.layoutInfo == layoutInfo); - XCTAssertTrue(uut.options == options); - XCTAssertTrue(uut.presenter == presenter); - XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count); -} - -- (void)testInitWithEventEmmiter_shouldInitializeDependencies { - RNNLayoutInfo *layoutInfo = [RNNLayoutInfo new]; - RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}]; - RNNBottomTabsPresenter *presenter = [[RNNBottomTabsPresenter alloc] init]; - RNNEventEmitter *eventEmmiter = [RNNEventEmitter new]; - - NSArray *childViewControllers = @[[UIViewController new]]; - - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:eventEmmiter childViewControllers:childViewControllers]; + RNNEventEmitter *eventEmmiter = [RNNEventEmitter new]; + + RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter bottomTabPresenter:nil dotIndicatorPresenter:nil eventEmitter:eventEmmiter childViewControllers:childViewControllers bottomTabsAttacher:nil]; + [uut viewWillAppear:YES]; XCTAssertTrue(uut.layoutInfo == layoutInfo); XCTAssertTrue(uut.options == options); XCTAssertTrue(uut.presenter == presenter); XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count); - XCTAssertTrue(uut.eventEmitter == eventEmmiter); + XCTAssertTrue(uut.eventEmitter == eventEmmiter); } - (void)testInitWithLayoutInfo_shouldSetDelegate { - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNBasePresenter alloc] init] eventEmitter:nil childViewControllers:nil]; + RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[]]; XCTAssertTrue(uut.delegate == uut); } @@ -179,7 +167,9 @@ - (void)testSetSelectedIndexByComponentID_ShouldSetSelectedIndexWithCorrectIndex RNNComponentViewController *vc = [[RNNComponentViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil]; - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:[RNNBottomTabsPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]]; + RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[[UIViewController new], vc]]; + [uut viewWillAppear:YES]; + [uut setSelectedIndexByComponentID:@"componentId"]; XCTAssertTrue(uut.selectedIndex == 1); } @@ -189,7 +179,8 @@ - (void)testSetSelectedIndex_ShouldSetSelectedIndexWithCurrentTabIndex { options.bottomTabs.currentTabIndex = [[IntNumber alloc] initWithValue:@(1)]; RNNComponentViewController *vc = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil]; - RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:nil presenter:[RNNBottomTabsPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]]; + RNNBottomTabsController *uut = [RNNBottomTabsController createWithChildren:@[[UIViewController new], vc] options:options]; + [uut viewWillAppear:YES]; XCTAssertTrue(uut.selectedIndex == 1); } diff --git a/playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m b/playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m index 5ae801d08b2..a79a415bb3b 100644 --- a/playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m +++ b/playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m @@ -26,6 +26,7 @@ - (void)setUp { self.dotIndicatorPresenter = [OCMockObject partialMockForObject:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:nil]]; self.uut = [OCMockObject partialMockForObject:[BottomTabsPresenterCreator createWithDefaultOptions:nil]]; 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]]; + [self.boundViewController viewWillAppear:YES]; [self.uut bindViewController:self.boundViewController]; self.options = [[RNNNavigationOptions alloc] initEmptyOptions]; } @@ -35,7 +36,8 @@ - (void)testApplyOptions_shouldSetDefaultEmptyOptions { [[self.boundViewController expect] setTabBarTestID:nil]; [[(id)self.uut expect] applyBackgroundColor:nil translucent:NO]; [[self.boundViewController expect] setTabBarHideShadow:NO]; - [[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault]; + [[self.boundViewController expect] setTabBarVisible:YES animated:NO]; + [[self.boundViewController expect] setTabBarStyle:UIBarStyleDefault]; [self.uut applyOptions:emptyOptions]; [self.boundViewController verify]; } @@ -58,6 +60,16 @@ - (void)testApplyOptions_shouldApplyOptions { [self.boundViewController verify]; } +- (void)testApplyOptions_shouldRestoreHiddenTabBar { + RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; + initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(1)]; + + [[self.boundViewController expect] setTabBarVisible:YES animated:NO]; + + [self.uut applyOptions:initialOptions]; + [self.boundViewController verify]; +} + - (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages { RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"]; diff --git a/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h b/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h index 35f6c9937a9..f7ba0d56841 100644 --- a/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h +++ b/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h @@ -7,4 +7,6 @@ + (RNNBottomTabsController *)createWithChildren:(NSArray *)children; ++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children options:(RNNNavigationOptions *)options; + @end diff --git a/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m b/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m index 58c099beffc..4d1b02fc541 100644 --- a/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m +++ b/playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m @@ -5,13 +5,16 @@ @implementation RNNBottomTabsController (Helpers) + (RNNBottomTabsController *)create { - RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; return [self createWithChildren:nil]; } + (RNNBottomTabsController *)createWithChildren:(NSArray *)children { + return [self createWithChildren:children options:[[RNNNavigationOptions alloc] initEmptyOptions]]; +} + ++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children options:(RNNNavigationOptions *)options { RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions]; - 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]; + 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]; } @end diff --git a/playground/ios/NavigationTests/RNNCommandsHandlerTest.m b/playground/ios/NavigationTests/RNNCommandsHandlerTest.m index d2ecf461c19..5703596bb7a 100644 --- a/playground/ios/NavigationTests/RNNCommandsHandlerTest.m +++ b/playground/ios/NavigationTests/RNNCommandsHandlerTest.m @@ -382,7 +382,7 @@ - (void)testSetRoot_withBottomTabsAttachModeOnSwitchToTab { BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options]; 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]; - + [tabBarController viewWillAppear:YES]; OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController); [self.uut setRoot:@{} commandId:@"" completion:^{}]; @@ -400,7 +400,8 @@ - (void)testSetRoot_withBottomTabsAttachModeAfterInitialTab { BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options]; 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]; - + [tabBarController viewWillAppear:YES]; + OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController); [self.uut setRoot:@{} commandId:@"" completion:^{ @@ -424,6 +425,7 @@ - (void)testMergeOptions_shouldMergeWithChildOnly { RNNComponentViewController* secondChild = [RNNComponentViewController createWithComponentId:@"second" initialOptions:secondChildOptions]; 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]; + [tabBarController viewWillAppear:YES]; OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController); [self.mainWindow setRootViewController:tabBarController]; @@ -455,6 +457,7 @@ - (void)testMergeOptions_shouldResolveTreeOptions { RNNComponentViewController* secondChild = [RNNComponentViewController createWithComponentId:@"second" initialOptions:secondChildOptions]; 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]; + [tabBarController viewWillAppear:YES]; OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController); [self.mainWindow setRootViewController:tabBarController]; diff --git a/playground/ios/NavigationTests/RNNControllerFactoryTest.m b/playground/ios/NavigationTests/RNNControllerFactoryTest.m index a674f45214f..77180948951 100644 --- a/playground/ios/NavigationTests/RNNControllerFactoryTest.m +++ b/playground/ios/NavigationTests/RNNControllerFactoryTest.m @@ -115,7 +115,7 @@ - (void)testCreateLayout_BottomTabsLayout { @"data": @{}, @"children": @[]}]}]}; RNNBottomTabsController* tabBar = (RNNBottomTabsController*) [self.factory createLayout:layout]; - + [tabBar viewWillAppear:YES]; XCTAssertTrue([tabBar isMemberOfClass:[RNNBottomTabsController class]]); XCTAssertTrue(tabBar.childViewControllers.count == 1); XCTAssertTrue([tabBar.childViewControllers[0] isMemberOfClass:[RNNStackController class]]); diff --git a/playground/ios/NavigationTests/RNNRootViewControllerTest.m b/playground/ios/NavigationTests/RNNRootViewControllerTest.m index 00ac960904e..27d791137e8 100644 --- a/playground/ios/NavigationTests/RNNRootViewControllerTest.m +++ b/playground/ios/NavigationTests/RNNRootViewControllerTest.m @@ -182,6 +182,7 @@ - (void)testTabBadge { NSMutableArray* controllers = [NSMutableArray new]; [controllers addObject:self.uut]; __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; + [vc viewWillAppear:NO]; [self.uut willMoveToParentViewController:vc]; XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]); @@ -369,9 +370,8 @@ - (void)testOrientationTabsController_portrait { self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; - - [self.uut viewWillAppear:false]; - + [vc viewWillAppear:NO]; + [self.uut viewWillAppear:NO]; UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskPortrait; XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation); } @@ -381,8 +381,8 @@ - (void)testOrientationTabsController_portraitAndLandscape { self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; - - [self.uut viewWillAppear:false]; + [vc viewWillAppear:NO]; + [self.uut viewWillAppear:NO]; UIInterfaceOrientationMask expectedOrientation = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape); XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation); @@ -393,8 +393,8 @@ - (void)testOrientationTabsController_all { self.options.layout.orientation = supportedOrientations; NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]]; __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers]; - - [self.uut viewWillAppear:false]; + [vc viewWillAppear:NO]; + [self.uut viewWillAppear:NO]; UIInterfaceOrientationMask expectedOrientation = UIInterfaceOrientationMaskAll; XCTAssertTrue(self.uut.tabBarController.supportedInterfaceOrientations == expectedOrientation); @@ -553,7 +553,7 @@ - (void)testOverrideOptions { - (RNNStackController *)createNavigationController { RNNStackController* nav = [[RNNStackController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNStackPresenter alloc] init] eventEmitter:nil childViewControllers:@[self.uut]]; - + return nav; } diff --git a/playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m b/playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m index f6ef159735a..c3432b79c4e 100644 --- a/playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m +++ b/playground/ios/NavigationTests/UIViewController+LayoutProtocolTest.m @@ -37,6 +37,24 @@ - (void)testInitWithLayoutInfoShouldSetChildViewControllers { NSArray* childViewControllers = @[child1, child2]; UINavigationController* uut = [[UINavigationController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers]; + XCTAssertEqual(uut.viewControllers.count, 2); +} + +- (void)testInitBottomTabsWithLayoutInfoShouldNotSetChildViewControllers { + UIViewController* child1 = [UIViewController new]; + UIViewController* child2 = [UIViewController new]; + NSArray* childViewControllers = @[child1, child2]; + RNNBottomTabsController* uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers]; + + XCTAssertEqual(uut.viewControllers.count, 0); +} + +- (void)testLoadChildrenShouldSetChildViewControllers { + UIViewController* child1 = [UIViewController new]; + UIViewController* child2 = [UIViewController new]; + NSArray* childViewControllers = @[child1, child2]; + UINavigationController* uut = [[UINavigationController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:childViewControllers]; + [uut loadChildren:childViewControllers]; XCTAssertEqual(uut.viewControllers[0], child1); XCTAssertEqual(uut.viewControllers[1], child2); } diff --git a/playground/src/screens/SetRootScreen.js b/playground/src/screens/SetRootScreen.js index 908ebab6125..3134923cc84 100644 --- a/playground/src/screens/SetRootScreen.js +++ b/playground/src/screens/SetRootScreen.js @@ -5,7 +5,10 @@ const Navigation = require('./../services/Navigation'); const { NAVIGATION_TAB, SET_MULTIPLE_ROOTS_BTN, - SET_ROOT_BTN + SET_ROOT_BTN, + LAYOUTS_TAB, + SET_ROOT_HIDES_BOTTOM_TABS_BTN, + SET_ROOT_WITH_STACK_HIDES_BOTTOM_TABS_BTN } = require('../testIDs'); const Screens = require('./Screens'); const { logLifecycleEvent } = require('./StaticLifecycleOverlay'); @@ -37,6 +40,8 @@ class SetRootScreen extends React.Component {