Browse Source

Detach bottomTabPresenter from basePresenter (#5970)

Refactor bottomTabs options applying mechanism. Both bottomTab and bottomTabs presenters were part of the base UIViewController class which was wrong. This commits moves them to the BottomTabsController.

This refactor mostly contains the following

* Detach bottomTabPresenter from basePresenter
* Create tabBarItem appearance on presenter init
* Refactor bottomTabPresenter
* Refactor bottomTabPresenter
* Move appearance creation to bottomTabsController
* Merge child options with direct child controller
Guy Carmeli 4 years ago
parent
commit
d2ba232d5e
No account linked to committer's email address
30 changed files with 279 additions and 208 deletions
  1. 2
    0
      lib/ios/BottomTabAppearancePresenter.h
  2. 8
    5
      lib/ios/BottomTabAppearancePresenter.m
  3. 6
    2
      lib/ios/BottomTabPresenter.h
  4. 13
    46
      lib/ios/BottomTabPresenter.m
  5. 1
    1
      lib/ios/BottomTabPresenterCreator.h
  6. 2
    2
      lib/ios/BottomTabPresenterCreator.m
  7. 0
    6
      lib/ios/BottomTabsAppearancePresenter.m
  8. 0
    2
      lib/ios/RNNBasePresenter.h
  9. 2
    15
      lib/ios/RNNBasePresenter.m
  10. 4
    0
      lib/ios/RNNBottomTabsController.h
  11. 26
    1
      lib/ios/RNNBottomTabsController.m
  12. 0
    2
      lib/ios/RNNBottomTabsPresenter.h
  13. 0
    12
      lib/ios/RNNBottomTabsPresenter.m
  14. 6
    1
      lib/ios/RNNControllerFactory.m
  15. 5
    0
      lib/ios/RNNDotIndicatorPresenter.h
  16. 27
    9
      lib/ios/RNNDotIndicatorPresenter.m
  17. 4
    0
      lib/ios/UIViewController+LayoutProtocol.h
  18. 22
    6
      lib/ios/UIViewController+LayoutProtocol.m
  19. 0
    22
      playground/ios/NavigationIOS12Tests/RNNBottomTabsPresenterTest.m
  20. 3
    3
      playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m
  21. 55
    0
      playground/ios/NavigationTests/BottomTabPresenterTest.m
  22. 2
    23
      playground/ios/NavigationTests/RNNBasePresenterTest.m
  23. 12
    31
      playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m
  24. 10
    0
      playground/ios/NavigationTests/RNNBottomTabsController+Helpers.h
  25. 17
    0
      playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m
  26. 3
    3
      playground/ios/NavigationTests/RNNCommandsHandlerTest.m
  27. 13
    4
      playground/ios/NavigationTests/RNNDotIndicatorPresenterTest.m
  28. 7
    8
      playground/ios/NavigationTests/RNNRootViewControllerTest.m
  29. 17
    4
      playground/ios/NavigationTests/RNNTabBarControllerTest.m
  30. 12
    0
      playground/ios/playground.xcodeproj/project.pbxproj

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

3
 API_AVAILABLE(ios(13.0))
3
 API_AVAILABLE(ios(13.0))
4
 @interface BottomTabAppearancePresenter : BottomTabPresenter
4
 @interface BottomTabAppearancePresenter : BottomTabPresenter
5
 
5
 
6
+- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions children:(NSArray<UIViewController *> *)children;
7
+
6
 @end
8
 @end

+ 8
- 5
lib/ios/BottomTabAppearancePresenter.m View File

3
 
3
 
4
 @implementation BottomTabAppearancePresenter
4
 @implementation BottomTabAppearancePresenter
5
 
5
 
6
-- (void)bindViewController:(UIViewController *)boundViewController {
7
-    [super bindViewController:boundViewController];
8
-    boundViewController.tabBarItem.standardAppearance = [[UITabBarAppearance alloc] init];
6
+- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions children:(NSArray<UIViewController *> *)children {
7
+    self = [super initWithDefaultOptions:defaultOptions];
8
+    for (UIViewController* child in children) {
9
+        child.tabBarItem.standardAppearance = [[UITabBarAppearance alloc] init];
10
+    }
11
+    return self;
9
 }
12
 }
10
 
13
 
11
-- (void)updateTabBarItem:(UITabBarItem *)tabItem bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
12
-    self.boundViewController.tabBarItem = [TabBarItemAppearanceCreator updateTabBarItem:self.boundViewController.tabBarItem bottomTabOptions:bottomTabOptions];
14
+- (void)createTabBarItem:(UIViewController *)child bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
15
+    child.tabBarItem = [TabBarItemAppearanceCreator updateTabBarItem:child.tabBarItem bottomTabOptions:bottomTabOptions];
13
 }
16
 }
14
 
17
 
15
 @end
18
 @end

+ 6
- 2
lib/ios/BottomTabPresenter.h View File

2
 
2
 
3
 @interface BottomTabPresenter : RNNBasePresenter
3
 @interface BottomTabPresenter : RNNBasePresenter
4
 
4
 
5
-- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions;
5
+- (void)applyOptions:(RNNNavigationOptions *)options child:(UIViewController *)child;
6
 
6
 
7
-- (void)applyDotIndicator:(UIViewController *)child;
7
+- (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options  child:(UIViewController *)child;
8
+
9
+- (void)createTabBarItem:(UIViewController *)child bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions;
10
+
11
+- (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions child:(UIViewController *)child;
8
 
12
 
9
 @end
13
 @end

+ 13
- 46
lib/ios/BottomTabPresenter.m View File

1
 #import "BottomTabPresenter.h"
1
 #import "BottomTabPresenter.h"
2
 #import "RNNTabBarItemCreator.h"
2
 #import "RNNTabBarItemCreator.h"
3
 #import "UIViewController+RNNOptions.h"
3
 #import "UIViewController+RNNOptions.h"
4
-#import "RNNDotIndicatorPresenter.h"
5
 #import "UIViewController+LayoutProtocol.h"
4
 #import "UIViewController+LayoutProtocol.h"
6
 
5
 
7
-@interface BottomTabPresenter ()
8
-@property(nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter;
9
-@end
10
-
11
 @implementation BottomTabPresenter
6
 @implementation BottomTabPresenter
12
 
7
 
13
-- (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions {
14
-    self = [super init];
15
-    self.defaultOptions = defaultOptions;
16
-    self.dotIndicatorPresenter = [[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:defaultOptions];
17
-    return self;
18
-}
19
-
20
-- (void)applyOptions:(RNNNavigationOptions *)options {
8
+- (void)applyOptions:(RNNNavigationOptions *)options child:(UIViewController *)child {
21
     RNNNavigationOptions * withDefault = [options withDefault:self.defaultOptions];
9
     RNNNavigationOptions * withDefault = [options withDefault:self.defaultOptions];
22
     
10
     
23
-    if (withDefault.bottomTab.badge.hasValue && [self.boundViewController.parentViewController isKindOfClass:[UITabBarController class]]) {
24
-        [self.boundViewController setTabBarItemBadge:withDefault.bottomTab.badge.get];
25
-    }
26
-    
27
-    if (withDefault.bottomTab.badgeColor.hasValue && [self.boundViewController.parentViewController isKindOfClass:[UITabBarController class]]) {
28
-        [self.boundViewController setTabBarItemBadgeColor:withDefault.bottomTab.badgeColor.get];
29
-    }
11
+    [child setTabBarItemBadge:[withDefault.bottomTab.badge getWithDefaultValue:[NSNull null]]];
12
+    [child setTabBarItemBadgeColor:[withDefault.bottomTab.badgeColor getWithDefaultValue:nil]];
30
 }
13
 }
31
 
14
 
32
-- (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
15
+- (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options  child:(UIViewController *)child {
33
     RNNNavigationOptions * withDefault = [options withDefault:self.defaultOptions];
16
     RNNNavigationOptions * withDefault = [options withDefault:self.defaultOptions];
34
     
17
     
35
-    if (withDefault.bottomTab.hasValue) {
36
-        [self updateTabBarItem:self.boundViewController.tabBarItem bottomTabOptions:withDefault.bottomTab];
37
-    }
18
+    [child setTabBarItemBadge:[withDefault.bottomTab.badge getWithDefaultValue:[NSNull null]]];
19
+    [child setTabBarItemBadgeColor:[withDefault.bottomTab.badgeColor getWithDefaultValue:nil]];
20
+    [self createTabBarItem:child bottomTabOptions:withDefault.bottomTab];
38
 }
21
 }
39
 
22
 
40
-- (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions {
23
+- (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions child:(UIViewController *)child {
41
     RNNNavigationOptions* withDefault = (RNNNavigationOptions *) [[resolvedOptions withDefault:self.defaultOptions] overrideOptions:options];
24
     RNNNavigationOptions* withDefault = (RNNNavigationOptions *) [[resolvedOptions withDefault:self.defaultOptions] overrideOptions:options];
42
     
25
     
43
-    if (options.bottomTab.badge.hasValue) {
44
-        [self.boundViewController setTabBarItemBadge:options.bottomTab.badge.get];
45
-    }
46
-    
47
-    if (options.bottomTab.badgeColor.hasValue && [self.boundViewController.parentViewController isKindOfClass:[UITabBarController class]]) {
48
-        [self.boundViewController setTabBarItemBadgeColor:options.bottomTab.badgeColor.get];
49
-    }
50
-    
51
-    if ([options.bottomTab.dotIndicator hasValue] && [self.boundViewController.parentViewController isKindOfClass:[UITabBarController class]]) {
52
-        [[self dotIndicatorPresenter] apply:self.boundViewController:options.bottomTab.dotIndicator];
53
-    }
54
-    
55
-    if (options.bottomTab.hasValue) {
56
-        [self updateTabBarItem:self.boundViewController.tabBarItem bottomTabOptions:withDefault.bottomTab];
57
-    }
58
-}
59
-
60
-- (void)updateTabBarItem:(UITabBarItem *)tabItem bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
61
-    self.boundViewController.tabBarItem = [RNNTabBarItemCreator updateTabBarItem:self.boundViewController.tabBarItem bottomTabOptions:bottomTabOptions];
26
+    if (options.bottomTab.badge.hasValue) [child setTabBarItemBadge:options.bottomTab.badge.get];
27
+    if (options.bottomTab.badgeColor.hasValue) [child setTabBarItemBadgeColor:options.bottomTab.badgeColor.get];
28
+    if (options.bottomTab.hasValue) [self createTabBarItem:child bottomTabOptions:withDefault.bottomTab];
62
 }
29
 }
63
 
30
 
64
-- (void)applyDotIndicator:(UIViewController *)child {
65
-    [_dotIndicatorPresenter apply:child:[child resolveOptions].bottomTab.dotIndicator];
31
+- (void)createTabBarItem:(UIViewController *)child bottomTabOptions:(RNNBottomTabOptions *)bottomTabOptions {
32
+    child.tabBarItem = [RNNTabBarItemCreator updateTabBarItem:child.tabBarItem bottomTabOptions:bottomTabOptions];
66
 }
33
 }
67
 
34
 
68
 @end
35
 @end

+ 1
- 1
lib/ios/BottomTabPresenterCreator.h View File

3
 
3
 
4
 @interface BottomTabPresenterCreator : NSObject
4
 @interface BottomTabPresenterCreator : NSObject
5
 
5
 
6
-+ (BottomTabPresenter *)createWithDefaultOptions:(RNNNavigationOptions *)defaultOptions;
6
++ (BottomTabPresenter *)createWithDefaultOptions:(RNNNavigationOptions *)defaultOptions children:(NSArray<UIViewController *> *)children;
7
 
7
 
8
 @end
8
 @end

+ 2
- 2
lib/ios/BottomTabPresenterCreator.m View File

3
 
3
 
4
 @implementation BottomTabPresenterCreator
4
 @implementation BottomTabPresenterCreator
5
 
5
 
6
-+ (BottomTabPresenter *)createWithDefaultOptions:(RNNNavigationOptions *)defaultOptions {
6
++ (BottomTabPresenter *)createWithDefaultOptions:(RNNNavigationOptions *)defaultOptions children:(NSArray<UIViewController *> *)children {
7
 	if (@available(iOS 13.0, *)) {
7
 	if (@available(iOS 13.0, *)) {
8
-		return [[BottomTabAppearancePresenter alloc] initWithDefaultOptions:defaultOptions];
8
+		return [[BottomTabAppearancePresenter alloc] initWithDefaultOptions:defaultOptions children:children];
9
 	} else {
9
 	} else {
10
 		return [[BottomTabPresenter alloc] initWithDefaultOptions:defaultOptions];
10
 		return [[BottomTabPresenter alloc] initWithDefaultOptions:defaultOptions];
11
 	}
11
 	}

+ 0
- 6
lib/ios/BottomTabsAppearancePresenter.m View File

2
 
2
 
3
 @implementation BottomTabsAppearancePresenter
3
 @implementation BottomTabsAppearancePresenter
4
 
4
 
5
-- (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
6
-    [super applyOptionsOnInit:options];
7
-    UITabBarController *bottomTabs = self.tabBarController;
8
-    bottomTabs.tabBar.standardAppearance = [UITabBarAppearance new];
9
-}
10
-
11
 - (void)setTabBarBackgroundColor:(UIColor *)backgroundColor {
5
 - (void)setTabBarBackgroundColor:(UIColor *)backgroundColor {
12
     UITabBarController *bottomTabs = self.tabBarController;
6
     UITabBarController *bottomTabs = self.tabBarController;
13
     for (UIViewController* childViewController in bottomTabs.childViewControllers) {
7
     for (UIViewController* childViewController in bottomTabs.childViewControllers) {

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

27
 
27
 
28
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options;
28
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options;
29
 
29
 
30
-- (void)applyDotIndicator:(UIViewController *)child;
31
-
32
 - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions;
30
 - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions;
33
 
31
 
34
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
32
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;

+ 2
- 15
lib/ios/RNNBasePresenter.m View File

5
 #import "UIViewController+LayoutProtocol.h"
5
 #import "UIViewController+LayoutProtocol.h"
6
 #import "DotIndicatorOptions.h"
6
 #import "DotIndicatorOptions.h"
7
 #import "RCTConvert+Modal.h"
7
 #import "RCTConvert+Modal.h"
8
-#import "BottomTabPresenterCreator.h"
9
 
8
 
10
-@interface RNNBasePresenter ()
11
-@property(nonatomic, strong) BottomTabPresenter* bottomTabPresenter;
12
-@end
13
 @implementation RNNBasePresenter
9
 @implementation RNNBasePresenter
14
 
10
 
15
 - (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions {
11
 - (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions {
16
     self = [super init];
12
     self = [super init];
17
     _defaultOptions = defaultOptions;
13
     _defaultOptions = defaultOptions;
18
-    _bottomTabPresenter = [BottomTabPresenterCreator createWithDefaultOptions:self.defaultOptions];
19
     return self;
14
     return self;
20
 }
15
 }
21
 
16
 
28
 - (void)bindViewController:(UIViewController *)boundViewController {
23
 - (void)bindViewController:(UIViewController *)boundViewController {
29
     self.boundComponentId = boundViewController.layoutInfo.componentId;
24
     self.boundComponentId = boundViewController.layoutInfo.componentId;
30
     _boundViewController = boundViewController;
25
     _boundViewController = boundViewController;
31
-    _bottomTabPresenter.boundViewController = boundViewController;
32
 }
26
 }
33
 
27
 
34
 - (void)setDefaultOptions:(RNNNavigationOptions *)defaultOptions {
28
 - (void)setDefaultOptions:(RNNNavigationOptions *)defaultOptions {
61
 }
55
 }
62
 
56
 
63
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
57
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
64
-    [_bottomTabPresenter applyOptionsOnWillMoveToParentViewController:options];
58
+
65
 }
59
 }
66
 
60
 
67
 - (void)applyOptions:(RNNNavigationOptions *)options {
61
 - (void)applyOptions:(RNNNavigationOptions *)options {
68
-    [_bottomTabPresenter applyOptions:options];
62
+
69
 }
63
 }
70
 
64
 
71
 - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions {
65
 - (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions {
72
-    UIViewController* viewController = self.boundViewController;
73
     RNNNavigationOptions* withDefault = (RNNNavigationOptions *) [[resolvedOptions withDefault:_defaultOptions] overrideOptions:options];
66
     RNNNavigationOptions* withDefault = (RNNNavigationOptions *) [[resolvedOptions withDefault:_defaultOptions] overrideOptions:options];
74
-    
75
-    [_bottomTabPresenter mergeOptions:options resolvedOptions:resolvedOptions];
76
 	
67
 	
77
 	if (options.window.backgroundColor.hasValue) {
68
 	if (options.window.backgroundColor.hasValue) {
78
 		UIApplication.sharedApplication.delegate.window.backgroundColor = withDefault.window.backgroundColor.get;
69
 		UIApplication.sharedApplication.delegate.window.backgroundColor = withDefault.window.backgroundColor.get;
90
 
81
 
91
 }
82
 }
92
 
83
 
93
-- (void)applyDotIndicator:(UIViewController *)child {
94
-    [_bottomTabPresenter applyDotIndicator:child];
95
-}
96
-
97
 - (UIStatusBarStyle)getStatusBarStyle:(RNNNavigationOptions *)resolvedOptions {
84
 - (UIStatusBarStyle)getStatusBarStyle:(RNNNavigationOptions *)resolvedOptions {
98
     RNNNavigationOptions *withDefault = [resolvedOptions withDefault:[self defaultOptions]];
85
     RNNNavigationOptions *withDefault = [resolvedOptions withDefault:[self defaultOptions]];
99
     NSString* statusBarStyle = [withDefault.statusBar.style getWithDefaultValue:@"default"];
86
     NSString* statusBarStyle = [withDefault.statusBar.style getWithDefaultValue:@"default"];

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

3
 #import "RNNBottomTabsPresenter.h"
3
 #import "RNNBottomTabsPresenter.h"
4
 #import "UIViewController+LayoutProtocol.h"
4
 #import "UIViewController+LayoutProtocol.h"
5
 #import "BottomTabsBaseAttacher.h"
5
 #import "BottomTabsBaseAttacher.h"
6
+#import "BottomTabPresenter.h"
7
+#import "RNNDotIndicatorPresenter.h"
6
 
8
 
7
 @interface RNNBottomTabsController : UITabBarController <RNNLayoutProtocol, UITabBarControllerDelegate>
9
 @interface RNNBottomTabsController : UITabBarController <RNNLayoutProtocol, UITabBarControllerDelegate>
8
 
10
 
11
                            options:(RNNNavigationOptions *)options
13
                            options:(RNNNavigationOptions *)options
12
                     defaultOptions:(RNNNavigationOptions *)defaultOptions
14
                     defaultOptions:(RNNNavigationOptions *)defaultOptions
13
                          presenter:(RNNBasePresenter *)presenter
15
                          presenter:(RNNBasePresenter *)presenter
16
+                bottomTabPresenter:(BottomTabPresenter *)bottomTabPresenter
17
+                dotIndicatorPresenter:(RNNDotIndicatorPresenter *)dotIndicatorPresenter
14
                       eventEmitter:(RNNEventEmitter *)eventEmitter
18
                       eventEmitter:(RNNEventEmitter *)eventEmitter
15
               childViewControllers:(NSArray *)childViewControllers
19
               childViewControllers:(NSArray *)childViewControllers
16
                 bottomTabsAttacher:(BottomTabsBaseAttacher *)bottomTabsAttacher;
20
                 bottomTabsAttacher:(BottomTabsBaseAttacher *)bottomTabsAttacher;

+ 26
- 1
lib/ios/RNNBottomTabsController.m View File

1
 #import "RNNBottomTabsController.h"
1
 #import "RNNBottomTabsController.h"
2
 #import "UITabBarController+RNNUtils.h"
2
 #import "UITabBarController+RNNUtils.h"
3
 
3
 
4
+@interface RNNBottomTabsController ()
5
+@property (nonatomic, strong) BottomTabPresenter* bottomTabPresenter;
6
+@property (nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter;
7
+@end
8
+
4
 @implementation RNNBottomTabsController {
9
 @implementation RNNBottomTabsController {
5
 	NSUInteger _currentTabIndex;
10
 	NSUInteger _currentTabIndex;
6
     BottomTabsBaseAttacher* _bottomTabsAttacher;
11
     BottomTabsBaseAttacher* _bottomTabsAttacher;
12
+    
7
 }
13
 }
8
 
14
 
9
 - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
15
 - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
11
                            options:(RNNNavigationOptions *)options
17
                            options:(RNNNavigationOptions *)options
12
                     defaultOptions:(RNNNavigationOptions *)defaultOptions
18
                     defaultOptions:(RNNNavigationOptions *)defaultOptions
13
                          presenter:(RNNBasePresenter *)presenter
19
                          presenter:(RNNBasePresenter *)presenter
20
+                bottomTabPresenter:(BottomTabPresenter *)bottomTabPresenter
21
+             dotIndicatorPresenter:(RNNDotIndicatorPresenter *)dotIndicatorPresenter
14
                       eventEmitter:(RNNEventEmitter *)eventEmitter
22
                       eventEmitter:(RNNEventEmitter *)eventEmitter
15
               childViewControllers:(NSArray *)childViewControllers
23
               childViewControllers:(NSArray *)childViewControllers
16
                 bottomTabsAttacher:(BottomTabsBaseAttacher *)bottomTabsAttacher {
24
                 bottomTabsAttacher:(BottomTabsBaseAttacher *)bottomTabsAttacher {
17
-    self = [super initWithLayoutInfo:layoutInfo creator:creator options:options defaultOptions:defaultOptions presenter:presenter eventEmitter:eventEmitter childViewControllers:childViewControllers];
18
     _bottomTabsAttacher = bottomTabsAttacher;
25
     _bottomTabsAttacher = bottomTabsAttacher;
26
+    _bottomTabPresenter = bottomTabPresenter;
27
+    _dotIndicatorPresenter = dotIndicatorPresenter;
28
+    self = [super initWithLayoutInfo:layoutInfo creator:creator options:options defaultOptions:defaultOptions presenter:presenter eventEmitter:eventEmitter childViewControllers:childViewControllers];
29
+    if (@available(iOS 13.0, *)) {
30
+        self.tabBar.standardAppearance = [UITabBarAppearance new];
31
+    }
19
     return self;
32
     return self;
20
 }
33
 }
21
 
34
 
35
+- (void)onChildAddToParent:(UIViewController *)child options:(RNNNavigationOptions *)options {
36
+    [_bottomTabPresenter applyOptionsOnWillMoveToParentViewController:options child:child];
37
+}
38
+
39
+- (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child {
40
+    [super mergeChildOptions:options child:child];
41
+    [_bottomTabPresenter mergeOptions:options resolvedOptions:self.resolveOptions child:[self findViewController:child]];
42
+    [_dotIndicatorPresenter mergeOptions:options resolvedOptions:self.resolveOptions child:[self findViewController:child]];
43
+}
44
+
22
 - (id<UITabBarControllerDelegate>)delegate {
45
 - (id<UITabBarControllerDelegate>)delegate {
23
 	return self;
46
 	return self;
24
 }
47
 }
36
               [view addGestureRecognizer: longPressGesture];
59
               [view addGestureRecognizer: longPressGesture];
37
           }
60
           }
38
     }
61
     }
62
+    
63
+    [_dotIndicatorPresenter bottomTabsDidLayoutSubviews:self];
39
 }
64
 }
40
 
65
 
41
 - (UIViewController *)getCurrentChild {
66
 - (UIViewController *)getCurrentChild {

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

2
 
2
 
3
 @interface RNNBottomTabsPresenter : RNNBasePresenter
3
 @interface RNNBottomTabsPresenter : RNNBasePresenter
4
 
4
 
5
-- (void)applyDotIndicator;
6
-
7
 - (void)setTabBarBackgroundColor:(UIColor *)backgroundColor;
5
 - (void)setTabBarBackgroundColor:(UIColor *)backgroundColor;
8
 
6
 
9
 - (UITabBarController *)tabBarController;
7
 - (UITabBarController *)tabBarController;

+ 0
- 12
lib/ios/RNNBottomTabsPresenter.m View File

88
     return self.tabBarController.tabBar;
88
     return self.tabBarController.tabBar;
89
 }
89
 }
90
 
90
 
91
-- (void)viewDidLayoutSubviews {
92
-    dispatch_async(dispatch_get_main_queue(), ^{
93
-        [self applyDotIndicator];
94
-    });
95
-}
96
-
97
-- (void)applyDotIndicator {
98
-    [self.boundViewController forEachChild:^(UIViewController *child) {
99
-        [self applyDotIndicator:child];
100
-    }];
101
-}
102
-
103
 @end
91
 @end

+ 6
- 1
lib/ios/RNNControllerFactory.m View File

9
 #import "BottomTabsBaseAttacher.h"
9
 #import "BottomTabsBaseAttacher.h"
10
 #import "BottomTabsAttachModeFactory.h"
10
 #import "BottomTabsAttachModeFactory.h"
11
 #import "BottomTabsPresenterCreator.h"
11
 #import "BottomTabsPresenterCreator.h"
12
+#import "BottomTabPresenterCreator.h"
12
 
13
 
13
 @implementation RNNControllerFactory {
14
 @implementation RNNControllerFactory {
14
 	id<RNNComponentViewCreator> _creator;
15
 	id<RNNComponentViewCreator> _creator;
150
     RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
151
     RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
151
     RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];
152
     RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];
152
     RNNBottomTabsPresenter* presenter = [BottomTabsPresenterCreator createWithDefaultOptions:_defaultOptions];
153
     RNNBottomTabsPresenter* presenter = [BottomTabsPresenterCreator createWithDefaultOptions:_defaultOptions];
154
+    NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
155
+    BottomTabPresenter* bottomTabPresenter = [BottomTabPresenterCreator createWithDefaultOptions:_defaultOptions children:childViewControllers];;
156
+    RNNDotIndicatorPresenter* dotIndicatorPresenter = [[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:_defaultOptions];
153
 	BottomTabsBaseAttacher* bottomTabsAttacher = [_bottomTabsAttachModeFactory fromOptions:options];
157
 	BottomTabsBaseAttacher* bottomTabsAttacher = [_bottomTabsAttachModeFactory fromOptions:options];
154
     
158
     
155
-    NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
156
     return [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo
159
     return [[RNNBottomTabsController alloc] initWithLayoutInfo:layoutInfo
157
                                                        creator:_creator
160
                                                        creator:_creator
158
                                                        options:options
161
                                                        options:options
159
                                                 defaultOptions:_defaultOptions
162
                                                 defaultOptions:_defaultOptions
160
                                                      presenter:presenter
163
                                                      presenter:presenter
164
+                                            bottomTabPresenter:bottomTabPresenter
165
+                                         dotIndicatorPresenter:dotIndicatorPresenter
161
                                                   eventEmitter:_eventEmitter
166
                                                   eventEmitter:_eventEmitter
162
                                           childViewControllers:childViewControllers
167
                                           childViewControllers:childViewControllers
163
                                             bottomTabsAttacher:bottomTabsAttacher
168
                                             bottomTabsAttacher:bottomTabsAttacher

+ 5
- 0
lib/ios/RNNDotIndicatorPresenter.h View File

8
 - (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions;
8
 - (instancetype)initWithDefaultOptions:(RNNNavigationOptions *)defaultOptions;
9
 
9
 
10
 - (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options;
10
 - (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options;
11
+
12
+- (void)bottomTabsDidLayoutSubviews:(UITabBarController *)bottomTabs;
13
+
14
+- (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions child:(UIViewController *)child;
15
+
11
 @end
16
 @end

+ 27
- 9
lib/ios/RNNDotIndicatorPresenter.m View File

13
     return self;
13
     return self;
14
 }
14
 }
15
 
15
 
16
+- (void)bottomTabsDidLayoutSubviews:(UITabBarController *)bottomTabs {
17
+    for (UIViewController* child in bottomTabs.childViewControllers) {
18
+        [self applyDotIndicator:child];
19
+    }
20
+}
21
+
22
+- (void)applyDotIndicator:(UIViewController *)child {
23
+    [self apply:child:[child resolveOptions].bottomTab.dotIndicator];
24
+}
25
+
26
+- (void)mergeOptions:(RNNNavigationOptions *)options resolvedOptions:(RNNNavigationOptions *)resolvedOptions child:(UIViewController *)child {
27
+    RNNNavigationOptions* withDefault = (RNNNavigationOptions *) [[resolvedOptions withDefault:self.defaultOptions] overrideOptions:options];
28
+    
29
+    if ([options.bottomTab.dotIndicator hasValue]) {
30
+        [self apply:child:withDefault.bottomTab.dotIndicator];
31
+    }
32
+}
33
+
16
 - (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options {
34
 - (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options {
17
     if (![options hasValue]) return;
35
     if (![options hasValue]) return;
18
-
36
+    
19
     if ([options.visible isFalse]) {
37
     if ([options.visible isFalse]) {
20
         if ([child tabBarItem].tag > 0) [self remove:child];
38
         if ([child tabBarItem].tag > 0) [self remove:child];
21
         return;
39
         return;
22
     }
40
     }
23
     if ([self currentIndicatorEquals:child :options]) return;
41
     if ([self currentIndicatorEquals:child :options]) return;
24
-
42
+    
25
     if ([self hasIndicator:child]) [self remove:child];
43
     if ([self hasIndicator:child]) [self remove:child];
26
-
44
+    
27
     UIView *indicator = [self createIndicator:options];
45
     UIView *indicator = [self createIndicator:options];
28
     [child tabBarItem].tag = indicator.tag;
46
     [child tabBarItem].tag = indicator.tag;
29
-
47
+    
30
     UITabBarController *bottomTabs = [self getTabBarController:child];
48
     UITabBarController *bottomTabs = [self getTabBarController:child];
31
     int index = (int) [[bottomTabs childViewControllers] indexOfObject:child];
49
     int index = (int) [[bottomTabs childViewControllers] indexOfObject:child];
32
     [[bottomTabs getTabView:index] addSubview:indicator];
50
     [[bottomTabs getTabView:index] addSubview:indicator];
47
     UIView *icon = [bottomTabs getTabIcon:index];
65
     UIView *icon = [bottomTabs getTabIcon:index];
48
     float size = [[options.size getWithDefaultValue:@6] floatValue];
66
     float size = [[options.size getWithDefaultValue:@6] floatValue];
49
     [NSLayoutConstraint activateConstraints:@[
67
     [NSLayoutConstraint activateConstraints:@[
50
-            [badge.leftAnchor constraintEqualToAnchor:icon.rightAnchor constant:-size / 2],
51
-            [badge.topAnchor constraintEqualToAnchor:icon.topAnchor constant:-size / 2],
52
-            [badge.widthAnchor constraintEqualToConstant:size],
53
-            [badge.heightAnchor constraintEqualToConstant:size]
68
+        [badge.leftAnchor constraintEqualToAnchor:icon.rightAnchor constant:-size / 2],
69
+        [badge.topAnchor constraintEqualToAnchor:icon.topAnchor constant:-size / 2],
70
+        [badge.widthAnchor constraintEqualToConstant:size],
71
+        [badge.heightAnchor constraintEqualToConstant:size]
54
     ]];
72
     ]];
55
 }
73
 }
56
 
74
 
80
     return [viewController isKindOfClass:[UITabBarController class]] ? viewController : [viewController tabBarController];
98
     return [viewController isKindOfClass:[UITabBarController class]] ? viewController : [viewController tabBarController];
81
 }
99
 }
82
 
100
 
83
-@end
101
+@end

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

12
 
12
 
13
 - (UIViewController *)presentedComponentViewController;
13
 - (UIViewController *)presentedComponentViewController;
14
 
14
 
15
+- (UIViewController *)findViewController:(UIViewController *)child;
16
+
15
 - (UIViewController *)topMostViewController;
17
 - (UIViewController *)topMostViewController;
16
 
18
 
17
 - (void)mergeOptions:(RNNNavigationOptions *)options;
19
 - (void)mergeOptions:(RNNNavigationOptions *)options;
18
 
20
 
19
 - (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child;
21
 - (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child;
20
 
22
 
23
+- (void)onChildAddToParent:(UIViewController *)child options:(RNNNavigationOptions *)options;
24
+
21
 - (UINavigationController *)stack;
25
 - (UINavigationController *)stack;
22
 
26
 
23
 - (RNNNavigationOptions *)resolveOptions;
27
 - (RNNNavigationOptions *)resolveOptions;

+ 22
- 6
lib/ios/UIViewController+LayoutProtocol.m View File

17
 	self.defaultOptions = defaultOptions;
17
 	self.defaultOptions = defaultOptions;
18
 	self.layoutInfo = layoutInfo;
18
 	self.layoutInfo = layoutInfo;
19
 	self.creator = creator;
19
 	self.creator = creator;
20
-	self.eventEmitter = eventEmitter;
21
-	if ([self respondsToSelector:@selector(setViewControllers:)]) {
22
-		[self performSelector:@selector(setViewControllers:) withObject:childViewControllers];
23
-	}
24
-	self.presenter = presenter;
20
+    self.eventEmitter = eventEmitter;
21
+    self.presenter = presenter;
25
     [self.presenter bindViewController:self];
22
     [self.presenter bindViewController:self];
26
-	[self.presenter applyOptionsOnInit:self.resolveOptions];
23
+    if ([self respondsToSelector:@selector(setViewControllers:)]) {
24
+        [self performSelector:@selector(setViewControllers:) withObject:childViewControllers];
25
+    }
26
+    [self.presenter applyOptionsOnInit:self.resolveOptions];
27
 
27
 
28
 	return self;
28
 	return self;
29
 }
29
 }
117
         return self;
117
         return self;
118
 }
118
 }
119
 
119
 
120
+- (UIViewController *)findViewController:(UIViewController *)child {
121
+    if (self == child) return child;
122
+    
123
+    for (UIViewController* childController in self.childViewControllers) {
124
+        UIViewController* fromChild = [childController findViewController:child];
125
+        if (fromChild) return childController;
126
+    }
127
+    
128
+    return nil;
129
+}
130
+
120
 - (CGFloat)getTopBarHeight {
131
 - (CGFloat)getTopBarHeight {
121
     for(UIViewController * child in [self childViewControllers]) {
132
     for(UIViewController * child in [self childViewControllers]) {
122
         CGFloat childTopBarHeight = [child getTopBarHeight];
133
         CGFloat childTopBarHeight = [child getTopBarHeight];
140
 	[self.parentViewController onChildWillAppear];
151
 	[self.parentViewController onChildWillAppear];
141
 }
152
 }
142
 
153
 
154
+- (void)onChildAddToParent:(UIViewController *)child options:(RNNNavigationOptions *)options {
155
+    [self.parentViewController onChildAddToParent:child options:options];
156
+}
157
+
143
 - (void)componentDidAppear {
158
 - (void)componentDidAppear {
144
     [self.presenter componentDidAppear];
159
     [self.presenter componentDidAppear];
145
     [self.parentViewController componentDidAppear];
160
     [self.parentViewController componentDidAppear];
153
 - (void)willMoveToParentViewController:(UIViewController *)parent {
168
 - (void)willMoveToParentViewController:(UIViewController *)parent {
154
 	if (parent) {
169
 	if (parent) {
155
 		[self.presenter applyOptionsOnWillMoveToParentViewController:self.resolveOptions];
170
 		[self.presenter applyOptionsOnWillMoveToParentViewController:self.resolveOptions];
171
+        [self onChildAddToParent:self options:self.resolveOptions];
156
 	}
172
 	}
157
 }
173
 }
158
 
174
 

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

70
 	[self.boundViewController verify];
70
 	[self.boundViewController verify];
71
 }
71
 }
72
 
72
 
73
-- (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
74
-    id uut = [self uut];
75
-    [[uut expect] applyDotIndicator];
76
-    [uut viewDidLayoutSubviews];
77
-    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
78
-    [uut verify];
79
-}
80
-
81
-- (void)testApplyDotIndicator_callsAppliesBadgeWithEachChild {
82
-    id uut = [self uut];
83
-    id child1 = [UIViewController new];
84
-    id child2 = [UIViewController new];
85
-
86
-    [[uut expect] applyDotIndicator:child1];
87
-    [[uut expect] applyDotIndicator:child2];
88
-    [[self boundViewController] addChildViewController:child1];
89
-    [[self boundViewController] addChildViewController:child2];
90
-
91
-    [uut applyDotIndicator];
92
-    [uut verify];
93
-}
94
-
95
 - (void)testBackgroundColor_validColor {
73
 - (void)testBackgroundColor_validColor {
96
 	UIColor* inputColor = [RCTConvert UIColor:@(0xFFFF0000)];
74
 	UIColor* inputColor = [RCTConvert UIColor:@(0xFFFF0000)];
97
 	self.options.layout.backgroundColor = [[Color alloc] initWithValue:inputColor];
75
 	self.options.layout.backgroundColor = [[Color alloc] initWithValue:inputColor];

+ 3
- 3
playground/ios/NavigationIOS12Tests/RNNRootViewControllerTest.m View File

8
 #import "RNNStackController.h"
8
 #import "RNNStackController.h"
9
 #import "RNNBottomTabsController.h"
9
 #import "RNNBottomTabsController.h"
10
 #import "RNNUIBarButtonItem.h"
10
 #import "RNNUIBarButtonItem.h"
11
+#import "RNNBottomTabsController+Helpers.h"
11
 
12
 
12
 
13
 
13
 @interface RNNComponentViewController (EmbedInTabBar)
14
 @interface RNNComponentViewController (EmbedInTabBar)
166
 -(void)testTabBadge {
167
 -(void)testTabBadge {
167
 	NSString* tabBadgeInput = @"5";
168
 	NSString* tabBadgeInput = @"5";
168
 	self.options.bottomTab.badge = [[Text alloc] initWithValue:tabBadgeInput];
169
 	self.options.bottomTab.badge = [[Text alloc] initWithValue:tabBadgeInput];
169
-	__unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] init];
170
 	NSMutableArray* controllers = [NSMutableArray new];
170
 	NSMutableArray* controllers = [NSMutableArray new];
171
 	UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"A Tab" image:nil tag:1];
171
 	UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"A Tab" image:nil tag:1];
172
 	[self.uut setTabBarItem:item];
172
 	[self.uut setTabBarItem:item];
173
 	[controllers addObject:self.uut];
173
 	[controllers addObject:self.uut];
174
-	[vc setViewControllers:controllers];
175
-	[self.uut viewWillAppear:false];
174
+	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
175
+	[self.uut willMoveToParentViewController:vc];
176
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
176
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
177
 
177
 
178
 }
178
 }

+ 55
- 0
playground/ios/NavigationTests/BottomTabPresenterTest.m View File

1
+#import <XCTest/XCTest.h>
2
+#import "RNNBasePresenter.h"
3
+#import <OCMock/OCMock.h>
4
+#import "UIViewController+RNNOptions.h"
5
+#import <ReactNativeNavigation/RNNComponentViewController.h>
6
+#import <ReactNativeNavigation/BottomTabAppearancePresenter.h>
7
+#import "RNNBottomTabsController+Helpers.h"
8
+
9
+@interface RNNBottomTabPresenterTest : XCTestCase
10
+
11
+@property(nonatomic, strong) BottomTabAppearancePresenter *uut;
12
+@property(nonatomic, strong) RNNNavigationOptions *options;
13
+@property(nonatomic, strong) RNNBottomTabsController *boundViewController;
14
+@property(nonatomic, strong) RNNComponentViewController *componentViewController;
15
+@property(nonatomic, strong) id mockBoundViewController;
16
+
17
+@end
18
+
19
+@implementation RNNBottomTabPresenterTest
20
+
21
+- (void)setUp {
22
+    [super setUp];
23
+    self.uut = [[BottomTabAppearancePresenter alloc] initWithDefaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
24
+	self.componentViewController = [RNNComponentViewController new];
25
+    self.boundViewController = [RNNBottomTabsController createWithChildren:@[self.componentViewController]];
26
+    self.mockBoundViewController = [OCMockObject partialMockForObject:self.boundViewController];
27
+	[self.uut bindViewController:self.mockBoundViewController];
28
+    self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
29
+}
30
+
31
+- (void)tearDown {
32
+    [super tearDown];
33
+    [self.mockBoundViewController stopMocking];
34
+    self.boundViewController = nil;
35
+}
36
+
37
+- (void)testApplyOptions_shouldSetTabBarItemBadgeWithValue {
38
+    self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
39
+    [self.uut applyOptions:self.options child:self.componentViewController];
40
+	XCTAssertEqual(self.componentViewController.tabBarItem.badgeValue, @"badge");
41
+}
42
+
43
+- (void)testMergeOptions_shouldSetTabBarItemColorWithDefaultOptions {
44
+	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
45
+	defaultOptions.bottomTab.selectedIconColor = [Color withColor:UIColor.greenColor];
46
+	self.uut.defaultOptions = defaultOptions;
47
+	
48
+	RNNNavigationOptions* mergeOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
49
+	mergeOptions.bottomTab.text = [[Text alloc] initWithValue:@"title"];
50
+	
51
+	[self.uut mergeOptions:mergeOptions resolvedOptions:self.options child:self.componentViewController];
52
+	XCTAssertEqual(self.componentViewController.tabBarItem.title, @"title");
53
+}
54
+
55
+@end

+ 2
- 23
playground/ios/NavigationTests/RNNBasePresenterTest.m View File

4
 #import "UIViewController+RNNOptions.h"
4
 #import "UIViewController+RNNOptions.h"
5
 #import "RNNComponentViewController.h"
5
 #import "RNNComponentViewController.h"
6
 
6
 
7
-@interface RNNBottomTabPresenterTest : XCTestCase
7
+@interface RNNBasePresenterTest : XCTestCase
8
 
8
 
9
 @property(nonatomic, strong) RNNBasePresenter *uut;
9
 @property(nonatomic, strong) RNNBasePresenter *uut;
10
 @property(nonatomic, strong) RNNNavigationOptions *options;
10
 @property(nonatomic, strong) RNNNavigationOptions *options;
13
 
13
 
14
 @end
14
 @end
15
 
15
 
16
-@implementation RNNBottomTabPresenterTest
16
+@implementation RNNBasePresenterTest
17
 
17
 
18
 - (void)setUp {
18
 - (void)setUp {
19
     [super setUp];
19
     [super setUp];
36
     [self.mockBoundViewController verify];
36
     [self.mockBoundViewController verify];
37
 }
37
 }
38
 
38
 
39
-- (void)testApplyOptions_shouldSetTabBarItemBadgeWithValue {
40
-    OCMStub([self.mockBoundViewController parentViewController]).andReturn([UITabBarController new]);
41
-    self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
42
-    [[self.mockBoundViewController expect] setTabBarItemBadge:self.options.bottomTab.badge.get];
43
-    [self.uut applyOptions:self.options];
44
-    [self.mockBoundViewController verify];
45
-}
46
-
47
 - (void)testApplyOptions_setTabBarItemBadgeShouldNotCalledOnUITabBarController {
39
 - (void)testApplyOptions_setTabBarItemBadgeShouldNotCalledOnUITabBarController {
48
     [self.uut bindViewController:self.mockBoundViewController];
40
     [self.uut bindViewController:self.mockBoundViewController];
49
     self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
41
     self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
96
 	XCTAssertTrue(_boundViewController.modalInPresentation);
88
 	XCTAssertTrue(_boundViewController.modalInPresentation);
97
 }
89
 }
98
 
90
 
99
-- (void)testMergeOptions_shouldSetTabBarItemColorWithDefaultOptions {
100
-	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
101
-	defaultOptions.bottomTab.selectedIconColor = [Color withColor:UIColor.greenColor];
102
-	self.uut.defaultOptions = defaultOptions;
103
-	
104
-	RNNNavigationOptions* mergeOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
105
-	mergeOptions.bottomTab.text = [[Text alloc] initWithValue:@"title"];
106
-    OCMStub([self.mockBoundViewController parentViewController]).andReturn([UITabBarController new]);
107
-	
108
-    [self.uut mergeOptions:mergeOptions resolvedOptions:self.options];
109
-	XCTAssertEqual(self.uut.boundViewController.tabBarItem.title, @"title");
110
-}
111
-
112
 @end
91
 @end

+ 12
- 31
playground/ios/NavigationTests/RNNBottomTabsAppearancePresenterTest.m View File

1
 #import <XCTest/XCTest.h>
1
 #import <XCTest/XCTest.h>
2
 #import <OCMock/OCMock.h>
2
 #import <OCMock/OCMock.h>
3
-#import "BottomTabsAppearancePresenter.h"
3
+#import "BottomTabsPresenterCreator.h"
4
+#import "BottomTabPresenterCreator.h"
4
 #import "UITabBarController+RNNOptions.h"
5
 #import "UITabBarController+RNNOptions.h"
5
 #import "RNNBottomTabsController.h"
6
 #import "RNNBottomTabsController.h"
6
 #import "RNNComponentViewController.h"
7
 #import "RNNComponentViewController.h"
8
+#import "RNNBottomTabsPresenter.h"
9
+#import "RNNDotIndicatorPresenter.h"
7
 
10
 
8
 @interface RNNBottomTabsAppearancePresenterTest : XCTestCase
11
 @interface RNNBottomTabsAppearancePresenterTest : XCTestCase
9
 
12
 
10
-@property(nonatomic, strong) BottomTabsAppearancePresenter *uut;
13
+@property(nonatomic, strong) RNNBottomTabsPresenter *uut;
14
+@property(nonatomic, strong) NSArray<UIViewController *> *children;
15
+@property(nonatomic, strong) id dotIndicatorPresenter;
11
 @property(nonatomic, strong) RNNNavigationOptions *options;
16
 @property(nonatomic, strong) RNNNavigationOptions *options;
12
 @property(nonatomic, strong) id boundViewController;
17
 @property(nonatomic, strong) id boundViewController;
13
 
18
 
17
 
22
 
18
 - (void)setUp {
23
 - (void)setUp {
19
     [super setUp];
24
     [super setUp];
20
-    self.uut = [OCMockObject partialMockForObject:[BottomTabsAppearancePresenter new]];
21
-    self.boundViewController = [OCMockObject partialMockForObject:[RNNBottomTabsController new]];
25
+	self.children = @[[[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNComponentPresenter alloc] initWithDefaultOptions:nil] options:nil defaultOptions:nil]];
26
+	self.dotIndicatorPresenter = [OCMockObject partialMockForObject:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:nil]];
27
+    self.uut = [OCMockObject partialMockForObject:[BottomTabsPresenterCreator createWithDefaultOptions:nil]];
28
+	self.boundViewController = [OCMockObject partialMockForObject:[[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:self.uut bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:nil children:self.children] dotIndicatorPresenter:self.dotIndicatorPresenter eventEmitter:nil childViewControllers:self.children bottomTabsAttacher:nil]];
22
     [self.uut bindViewController:self.boundViewController];
29
     [self.uut bindViewController:self.boundViewController];
23
     self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
30
     self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
24
 }
31
 }
71
 	[self.boundViewController verify];
78
 	[self.boundViewController verify];
72
 }
79
 }
73
 
80
 
74
-- (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
75
-    id uut = [self uut];
76
-    [[uut expect] applyDotIndicator];
77
-    [uut viewDidLayoutSubviews];
78
-    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
79
-    [uut verify];
80
-}
81
-
82
-- (void)testApplyDotIndicator_callsAppliesBadgeWithEachChild {
83
-    id uut = [self uut];
84
-    id child1 = [UIViewController new];
85
-    id child2 = [UIViewController new];
86
-
87
-    [[uut expect] applyDotIndicator:child1];
88
-    [[uut expect] applyDotIndicator:child2];
89
-    [[self boundViewController] addChildViewController:child1];
90
-    [[self boundViewController] addChildViewController:child2];
91
-
92
-    [uut applyDotIndicator];
93
-    [uut verify];
94
-}
95
-
96
 - (void)testBackgroundColor_validColor {
81
 - (void)testBackgroundColor_validColor {
97
 	UIColor* inputColor = [RCTConvert UIColor:@(0xFFFF0000)];
82
 	UIColor* inputColor = [RCTConvert UIColor:@(0xFFFF0000)];
98
 	self.options.layout.backgroundColor = [[Color alloc] initWithValue:inputColor];
83
 	self.options.layout.backgroundColor = [[Color alloc] initWithValue:inputColor];
103
 
88
 
104
 - (void)testTabBarBackgroundColor {
89
 - (void)testTabBarBackgroundColor {
105
 	UIColor* tabBarBackgroundColor = [UIColor redColor];
90
 	UIColor* tabBarBackgroundColor = [UIColor redColor];
106
-	RNNComponentPresenter* vcPresenter = [[RNNComponentPresenter alloc] initWithDefaultOptions:nil];
107
-	UIViewController* vc = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:vcPresenter options:nil defaultOptions:nil];
108
-	
109
-	[((UITabBarController *)self.boundViewController) setViewControllers:@[vc]];
110
 	[self.uut setTabBarBackgroundColor:tabBarBackgroundColor];
91
 	[self.uut setTabBarBackgroundColor:tabBarBackgroundColor];
111
-	XCTAssertTrue([vc.tabBarItem.standardAppearance.backgroundColor isEqual:tabBarBackgroundColor]);
92
+	XCTAssertTrue([self.children.lastObject.tabBarItem.standardAppearance.backgroundColor isEqual:tabBarBackgroundColor]);
112
 }
93
 }
113
 
94
 
114
 @end
95
 @end

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

1
+#import <ReactNativeNavigation/ReactNativeNavigation.h>
2
+#import "RNNBottomTabsController.h"
3
+
4
+@interface RNNBottomTabsController (Helpers)
5
+
6
++ (RNNBottomTabsController *)create;
7
+
8
++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children;
9
+
10
+@end

+ 17
- 0
playground/ios/NavigationTests/RNNBottomTabsController+Helpers.m View File

1
+#import "RNNBottomTabsController+Helpers.h"
2
+#import "BottomTabsPresenterCreator.h"
3
+#import "BottomTabPresenterCreator.h"
4
+
5
+@implementation RNNBottomTabsController (Helpers)
6
+
7
++ (RNNBottomTabsController *)create {
8
+	RNNNavigationOptions* defaultOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
9
+	return [self createWithChildren:nil];
10
+}
11
+
12
++ (RNNBottomTabsController *)createWithChildren:(NSArray *)children {
13
+	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 children:children] dotIndicatorPresenter:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:defaultOptions] eventEmitter:nil childViewControllers:children bottomTabsAttacher:nil];
15
+}
16
+
17
+@end

+ 3
- 3
playground/ios/NavigationTests/RNNCommandsHandlerTest.m View File

372
 	options.bottomTabs.tabsAttachMode = [[BottomTabsAttachMode alloc] initWithValue:@"together"];
372
 	options.bottomTabs.tabsAttachMode = [[BottomTabsAttachMode alloc] initWithValue:@"together"];
373
 
373
 
374
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
374
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
375
-	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] eventEmitter:_eventEmmiter childViewControllers:@[_vc1, _vc2] bottomTabsAttacher:attacher];
375
+	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];
376
 
376
 
377
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
377
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
378
 	
378
 	
390
 	options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES];
390
 	options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES];
391
 	
391
 	
392
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
392
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
393
-	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] eventEmitter:_eventEmmiter childViewControllers:@[_vc1, _vc2] bottomTabsAttacher:attacher];
393
+	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];
394
 
394
 
395
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
395
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
396
 	
396
 	
408
 	options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES];
408
 	options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES];
409
 
409
 
410
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
410
 	BottomTabsBaseAttacher* attacher = [[[BottomTabsAttachModeFactory alloc] initWithDefaultOptions:nil] fromOptions:options];
411
-	RNNBottomTabsController* tabBarController = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions] presenter:[RNNBasePresenter new] eventEmitter:_eventEmmiter childViewControllers:@[_vc1, _vc2] bottomTabsAttacher:attacher];
411
+	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];
412
 
412
 
413
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
413
 	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(tabBarController);
414
 
414
 

+ 13
- 4
playground/ios/NavigationTests/RNNDotIndicatorPresenterTest.m View File

3
 #import "RNNDotIndicatorPresenter.h"
3
 #import "RNNDotIndicatorPresenter.h"
4
 #import "DotIndicatorOptions.h"
4
 #import "DotIndicatorOptions.h"
5
 #import "RNNBottomTabsController.h"
5
 #import "RNNBottomTabsController.h"
6
-#import "RNNComponentViewController.h"
6
+#import <ReactNativeNavigation/RNNComponentViewController.h>
7
 #import "RNNTestBase.h"
7
 #import "RNNTestBase.h"
8
 #import "UITabBarController+RNNUtils.h"
8
 #import "UITabBarController+RNNUtils.h"
9
+#import <ReactNativeNavigation/BottomTabPresenterCreator.h>
10
+#import "RNNBottomTabsController+Helpers.h"
9
 
11
 
10
 @interface RNNDotIndicatorPresenterTest : RNNTestBase
12
 @interface RNNDotIndicatorPresenterTest : RNNTestBase
11
 @property(nonatomic, strong) id uut;
13
 @property(nonatomic, strong) id uut;
12
 @property(nonatomic, strong) RNNComponentViewController *child;
14
 @property(nonatomic, strong) RNNComponentViewController *child;
13
 @property(nonatomic, strong) id bottomTabs;
15
 @property(nonatomic, strong) id bottomTabs;
16
+@property(nonatomic, strong) BottomTabPresenter* bottomTabPresenter;
14
 @end
17
 @end
15
 
18
 
16
 @implementation RNNDotIndicatorPresenterTest
19
 @implementation RNNDotIndicatorPresenterTest
17
 - (void)setUp {
20
 - (void)setUp {
18
     [super setUp];
21
     [super setUp];
22
+	self.child = [self createChild];
23
+	self.bottomTabPresenter = [BottomTabPresenterCreator createWithDefaultOptions:nil children:@[self.child]];
19
     self.uut = [OCMockObject partialMockForObject:[RNNDotIndicatorPresenter new]];
24
     self.uut = [OCMockObject partialMockForObject:[RNNDotIndicatorPresenter new]];
20
-    self.bottomTabs = [OCMockObject partialMockForObject:[RNNBottomTabsController new]];
21
-    self.child = [self createChild];
22
-    [self.bottomTabs addChildViewController:self.child];
25
+    self.bottomTabs = [OCMockObject partialMockForObject:[RNNBottomTabsController createWithChildren:@[self.child]]];
23
 
26
 
24
     [self setupTopLevelUI:self.bottomTabs];
27
     [self setupTopLevelUI:self.bottomTabs];
25
 }
28
 }
128
     XCTAssertEqual([sizeConstraints[1] constant], 8);
131
     XCTAssertEqual([sizeConstraints[1] constant], 8);
129
 }
132
 }
130
 
133
 
134
+- (void)testApply_onBottomTabsViewDidLayout {
135
+	[[self.uut expect] apply:self.child :self.child.resolveOptions.bottomTab.dotIndicator];
136
+	[self.uut bottomTabsDidLayoutSubviews:self.bottomTabs];
137
+	[self.uut verify];
138
+}
139
+
131
 - (void)applyIndicator {
140
 - (void)applyIndicator {
132
     [self applyIndicator:[UIColor redColor]];
141
     [self applyIndicator:[UIColor redColor]];
133
 }
142
 }

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

8
 #import "RNNStackController.h"
8
 #import "RNNStackController.h"
9
 #import "RNNBottomTabsController.h"
9
 #import "RNNBottomTabsController.h"
10
 #import "RNNUIBarButtonItem.h"
10
 #import "RNNUIBarButtonItem.h"
11
-
11
+#import "RNNBottomTabsController+Helpers.h"
12
 
12
 
13
 @interface RNNComponentViewController (EmbedInTabBar)
13
 @interface RNNComponentViewController (EmbedInTabBar)
14
 - (void)embedInTabBarController;
14
 - (void)embedInTabBarController;
17
 @implementation RNNComponentViewController (EmbedInTabBar)
17
 @implementation RNNComponentViewController (EmbedInTabBar)
18
 
18
 
19
 - (void)embedInTabBarController {
19
 - (void)embedInTabBarController {
20
-	RNNBottomTabsController* tabVC = [[RNNBottomTabsController alloc] init];
20
+	RNNBottomTabsController* tabVC = [RNNBottomTabsController create];
21
 	tabVC.viewControllers = @[self];
21
 	tabVC.viewControllers = @[self];
22
 	[self viewWillAppear:false];
22
 	[self viewWillAppear:false];
23
 }
23
 }
179
 - (void)testTabBadge {
179
 - (void)testTabBadge {
180
 	NSString* tabBadgeInput = @"5";
180
 	NSString* tabBadgeInput = @"5";
181
 	self.options.bottomTab.badge = [[Text alloc] initWithValue:tabBadgeInput];
181
 	self.options.bottomTab.badge = [[Text alloc] initWithValue:tabBadgeInput];
182
-	__unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] init];
183
 	NSMutableArray* controllers = [NSMutableArray new];
182
 	NSMutableArray* controllers = [NSMutableArray new];
184
 	UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"A Tab" image:nil tag:1];
183
 	UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"A Tab" image:nil tag:1];
185
 	[self.uut setTabBarItem:item];
184
 	[self.uut setTabBarItem:item];
186
 	[controllers addObject:self.uut];
185
 	[controllers addObject:self.uut];
187
-	[vc setViewControllers:controllers];
188
-	[self.uut viewWillAppear:false];
186
+	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
187
+	[self.uut willMoveToParentViewController:vc];
189
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
188
 	XCTAssertTrue([self.uut.tabBarItem.badgeValue isEqualToString:tabBadgeInput]);
190
 
189
 
191
 }
190
 }
371
 	NSArray* supportedOrientations = @[@"portrait"];
370
 	NSArray* supportedOrientations = @[@"portrait"];
372
 	self.options.layout.orientation = supportedOrientations;
371
 	self.options.layout.orientation = supportedOrientations;
373
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
372
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
374
-    __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
373
+    __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
375
 
374
 
376
 	[self.uut viewWillAppear:false];
375
 	[self.uut viewWillAppear:false];
377
 
376
 
383
 	NSArray* supportedOrientations = @[@"portrait", @"landscape"];
382
 	NSArray* supportedOrientations = @[@"portrait", @"landscape"];
384
 	self.options.layout.orientation = supportedOrientations;
383
 	self.options.layout.orientation = supportedOrientations;
385
     NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
384
     NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
386
-    __unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
385
+    __unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
387
 
386
 
388
 	[self.uut viewWillAppear:false];
387
 	[self.uut viewWillAppear:false];
389
 
388
 
395
 	NSArray* supportedOrientations = @[@"all"];
394
 	NSArray* supportedOrientations = @[@"all"];
396
 	self.options.layout.orientation = supportedOrientations;
395
 	self.options.layout.orientation = supportedOrientations;
397
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
396
 	NSMutableArray* controllers = [[NSMutableArray alloc] initWithArray:@[self.uut]];
398
-	__unused RNNBottomTabsController* vc = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[RNNComponentPresenter new] eventEmitter:nil childViewControllers:controllers];
397
+	__unused RNNBottomTabsController* vc = [RNNBottomTabsController createWithChildren:controllers];
399
 
398
 
400
 	[self.uut viewWillAppear:false];
399
 	[self.uut viewWillAppear:false];
401
 
400
 

+ 17
- 4
playground/ios/NavigationTests/RNNTabBarControllerTest.m View File

1
 #import <XCTest/XCTest.h>
1
 #import <XCTest/XCTest.h>
2
-#import "RNNBottomTabsController.h"
3
-#import "RNNComponentViewController.h"
2
+#import <ReactNativeNavigation/RNNBottomTabsController.h>
3
+#import <ReactNativeNavigation/RNNComponentViewController.h>
4
 #import "RNNStackController.h"
4
 #import "RNNStackController.h"
5
 #import <OCMock/OCMock.h>
5
 #import <OCMock/OCMock.h>
6
 #import "RCTConvert+Modal.h"
6
 #import "RCTConvert+Modal.h"
7
+#import <ReactNativeNavigation/BottomTabPresenterCreator.h>
8
+#import "RNNBottomTabsController+Helpers.h"
7
 
9
 
8
 @interface RNNTabBarControllerTest : XCTestCase
10
 @interface RNNTabBarControllerTest : XCTestCase
9
 
11
 
22
 
24
 
23
     id tabBarClassMock = OCMClassMock([RNNBottomTabsController class]);
25
     id tabBarClassMock = OCMClassMock([RNNBottomTabsController class]);
24
     OCMStub([tabBarClassMock parentViewController]).andReturn([OCMockObject partialMockForObject:[RNNBottomTabsController new]]);
26
     OCMStub([tabBarClassMock parentViewController]).andReturn([OCMockObject partialMockForObject:[RNNBottomTabsController new]]);
25
-
27
+	NSArray* children = @[[[UIViewController alloc] init]];
26
     self.mockTabBarPresenter = [OCMockObject partialMockForObject:[[RNNBottomTabsPresenter alloc] init]];
28
     self.mockTabBarPresenter = [OCMockObject partialMockForObject:[[RNNBottomTabsPresenter alloc] init]];
27
     self.mockChildViewController = [OCMockObject partialMockForObject:[RNNComponentViewController new]];
29
     self.mockChildViewController = [OCMockObject partialMockForObject:[RNNComponentViewController new]];
28
     self.mockEventEmitter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
30
     self.mockEventEmitter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
29
-	self.originalUut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:self.mockTabBarPresenter eventEmitter:self.mockEventEmitter childViewControllers:@[[[UIViewController alloc] init]]];
31
+	self.originalUut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:self.mockTabBarPresenter bottomTabPresenter:[BottomTabPresenterCreator createWithDefaultOptions:nil children:children] dotIndicatorPresenter:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:nil] eventEmitter:self.mockEventEmitter childViewControllers:children bottomTabsAttacher:nil];
30
     self.uut = [OCMockObject partialMockForObject:self.originalUut];
32
     self.uut = [OCMockObject partialMockForObject:self.originalUut];
31
     OCMStub([self.uut selectedViewController]).andReturn(self.mockChildViewController);
33
     OCMStub([self.uut selectedViewController]).andReturn(self.mockChildViewController);
32
 }
34
 }
171
     XCTAssertTrue(uut.selectedIndex == 1);
173
     XCTAssertTrue(uut.selectedIndex == 1);
172
 }
174
 }
173
 
175
 
176
+- (void)testOnViewDidLayoutSubviews_ShouldUpdateDotIndicatorForChildren {
177
+	id dotIndicator = [OCMockObject partialMockForObject:[[RNNDotIndicatorPresenter alloc] initWithDefaultOptions:nil]];
178
+    RNNComponentViewController *vc = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
179
+	RNNBottomTabsController *uut = [[RNNBottomTabsController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil bottomTabPresenter:nil dotIndicatorPresenter:dotIndicator eventEmitter:nil childViewControllers:@[[UIViewController new], vc] bottomTabsAttacher:nil];
180
+	
181
+	[[dotIndicator expect] bottomTabsDidLayoutSubviews:uut];
182
+	[uut viewDidLayoutSubviews];
183
+	[dotIndicator verify];
184
+	
185
+}
186
+
174
 
187
 
175
 @end
188
 @end

+ 12
- 0
playground/ios/playground.xcodeproj/project.pbxproj View File

11
 		13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
11
 		13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
12
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
12
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
13
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
14
+		500E9FE72406A52100C61231 /* BottomTabPresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 500E9FE62406A4E200C61231 /* BottomTabPresenterTest.m */; };
14
 		501C86B9239FE9C400E0B631 /* UIImage+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 501C86B8239FE9C400E0B631 /* UIImage+Utils.m */; };
15
 		501C86B9239FE9C400E0B631 /* UIImage+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 501C86B8239FE9C400E0B631 /* UIImage+Utils.m */; };
15
 		5022EDCD2405522000852BA6 /* RNNBottomTabsPresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E58D263A2385888C003F36BA /* RNNBottomTabsPresenterTest.m */; };
16
 		5022EDCD2405522000852BA6 /* RNNBottomTabsPresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E58D263A2385888C003F36BA /* RNNBottomTabsPresenterTest.m */; };
16
 		5022EDCE2405524700852BA6 /* RNNBottomTabsAppearancePresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5022EDCB240551EE00852BA6 /* RNNBottomTabsAppearancePresenterTest.m */; };
17
 		5022EDCE2405524700852BA6 /* RNNBottomTabsAppearancePresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5022EDCB240551EE00852BA6 /* RNNBottomTabsAppearancePresenterTest.m */; };
19
 		50996C6823AA477400008F89 /* RNNRootViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50996C6723AA477400008F89 /* RNNRootViewControllerTest.m */; };
20
 		50996C6823AA477400008F89 /* RNNRootViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50996C6723AA477400008F89 /* RNNRootViewControllerTest.m */; };
20
 		50996C6923AA487800008F89 /* RNNTestRootViewCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = E58D263D2385888C003F36BA /* RNNTestRootViewCreator.m */; };
21
 		50996C6923AA487800008F89 /* RNNTestRootViewCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = E58D263D2385888C003F36BA /* RNNTestRootViewCreator.m */; };
21
 		50BCB27623F1A2B100D6C8E5 /* TopBarAppearancePresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */; };
22
 		50BCB27623F1A2B100D6C8E5 /* TopBarAppearancePresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */; };
23
+		50C9A8D1240EB95F00BD699F /* RNNBottomTabsController+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CF233C240695B10098042D /* RNNBottomTabsController+Helpers.m */; };
24
+		50CF233D240695B10098042D /* RNNBottomTabsController+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CF233C240695B10098042D /* RNNBottomTabsController+Helpers.m */; };
22
 		50C9A8D4240FB9D000BD699F /* RNNComponentViewController+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 50C9A8D3240FB9D000BD699F /* RNNComponentViewController+Utils.m */; };
25
 		50C9A8D4240FB9D000BD699F /* RNNComponentViewController+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 50C9A8D3240FB9D000BD699F /* RNNComponentViewController+Utils.m */; };
23
 		67C681D42B662A53F29C19DA /* Pods_NavigationIOS12Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE0B5D45FD34FBABC6586CF /* Pods_NavigationIOS12Tests.framework */; };
26
 		67C681D42B662A53F29C19DA /* Pods_NavigationIOS12Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE0B5D45FD34FBABC6586CF /* Pods_NavigationIOS12Tests.framework */; };
24
 		9D204F3DC4FBCD81583BF99F /* Pods_playground.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A3340545EAAF11C1F146864 /* Pods_playground.framework */; };
27
 		9D204F3DC4FBCD81583BF99F /* Pods_playground.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A3340545EAAF11C1F146864 /* Pods_playground.framework */; };
81
 		4259AF43A23D928FE78B4A3A /* Pods-NavigationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NavigationTests.debug.xcconfig"; path = "Target Support Files/Pods-NavigationTests/Pods-NavigationTests.debug.xcconfig"; sourceTree = "<group>"; };
84
 		4259AF43A23D928FE78B4A3A /* Pods-NavigationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NavigationTests.debug.xcconfig"; path = "Target Support Files/Pods-NavigationTests/Pods-NavigationTests.debug.xcconfig"; sourceTree = "<group>"; };
82
 		4A3340545EAAF11C1F146864 /* Pods_playground.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_playground.framework; sourceTree = BUILT_PRODUCTS_DIR; };
85
 		4A3340545EAAF11C1F146864 /* Pods_playground.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_playground.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83
 		4AE37ACF6BFBAB211EE8E7E9 /* Pods-NavigationIOS12Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NavigationIOS12Tests.release.xcconfig"; path = "Target Support Files/Pods-NavigationIOS12Tests/Pods-NavigationIOS12Tests.release.xcconfig"; sourceTree = "<group>"; };
86
 		4AE37ACF6BFBAB211EE8E7E9 /* Pods-NavigationIOS12Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NavigationIOS12Tests.release.xcconfig"; path = "Target Support Files/Pods-NavigationIOS12Tests/Pods-NavigationIOS12Tests.release.xcconfig"; sourceTree = "<group>"; };
87
+		500E9FE62406A4E200C61231 /* BottomTabPresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BottomTabPresenterTest.m; sourceTree = "<group>"; };
84
 		501C86B7239FE9C400E0B631 /* UIImage+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+Utils.h"; sourceTree = "<group>"; };
88
 		501C86B7239FE9C400E0B631 /* UIImage+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+Utils.h"; sourceTree = "<group>"; };
85
 		501C86B8239FE9C400E0B631 /* UIImage+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Utils.m"; sourceTree = "<group>"; };
89
 		501C86B8239FE9C400E0B631 /* UIImage+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Utils.m"; sourceTree = "<group>"; };
86
 		5022EDCB240551EE00852BA6 /* RNNBottomTabsAppearancePresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBottomTabsAppearancePresenterTest.m; sourceTree = "<group>"; };
90
 		5022EDCB240551EE00852BA6 /* RNNBottomTabsAppearancePresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBottomTabsAppearancePresenterTest.m; sourceTree = "<group>"; };
93
 		50996C6123AA46DD00008F89 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
97
 		50996C6123AA46DD00008F89 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
94
 		50996C6723AA477400008F89 /* RNNRootViewControllerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNRootViewControllerTest.m; sourceTree = "<group>"; };
98
 		50996C6723AA477400008F89 /* RNNRootViewControllerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNRootViewControllerTest.m; sourceTree = "<group>"; };
95
 		50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TopBarAppearancePresenterTest.m; sourceTree = "<group>"; };
99
 		50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TopBarAppearancePresenterTest.m; sourceTree = "<group>"; };
100
+		50CF233B240695B10098042D /* RNNBottomTabsController+Helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNNBottomTabsController+Helpers.h"; sourceTree = "<group>"; };
101
+		50CF233C240695B10098042D /* RNNBottomTabsController+Helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RNNBottomTabsController+Helpers.m"; sourceTree = "<group>"; };
96
 		50C9A8D2240FB9D000BD699F /* RNNComponentViewController+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNNComponentViewController+Utils.h"; sourceTree = "<group>"; };
102
 		50C9A8D2240FB9D000BD699F /* RNNComponentViewController+Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNNComponentViewController+Utils.h"; sourceTree = "<group>"; };
97
 		50C9A8D3240FB9D000BD699F /* RNNComponentViewController+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RNNComponentViewController+Utils.m"; sourceTree = "<group>"; };
103
 		50C9A8D3240FB9D000BD699F /* RNNComponentViewController+Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RNNComponentViewController+Utils.m"; sourceTree = "<group>"; };
98
 		7F8E255E2E08F6ECE7DF6FE3 /* Pods-playground.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-playground.release.xcconfig"; path = "Target Support Files/Pods-playground/Pods-playground.release.xcconfig"; sourceTree = "<group>"; };
104
 		7F8E255E2E08F6ECE7DF6FE3 /* Pods-playground.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-playground.release.xcconfig"; path = "Target Support Files/Pods-playground/Pods-playground.release.xcconfig"; sourceTree = "<group>"; };
255
 			children = (
261
 			children = (
256
 				E58D26442385888C003F36BA /* Options */,
262
 				E58D26442385888C003F36BA /* Options */,
257
 				E58D26422385888C003F36BA /* RNNBasePresenterTest.m */,
263
 				E58D26422385888C003F36BA /* RNNBasePresenterTest.m */,
264
+				500E9FE62406A4E200C61231 /* BottomTabPresenterTest.m */,
258
 				50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */,
265
 				50BCB27523F1A26600D6C8E5 /* TopBarAppearancePresenterTest.m */,
259
 				E58D26412385888C003F36BA /* RNNCommandsHandlerTest.m */,
266
 				E58D26412385888C003F36BA /* RNNCommandsHandlerTest.m */,
260
 				E58D26352385888B003F36BA /* RNNComponentPresenterTest.m */,
267
 				E58D26352385888B003F36BA /* RNNComponentPresenterTest.m */,
282
 				E58D263B2385888C003F36BA /* UITabBarController+RNNOptionsTest.m */,
289
 				E58D263B2385888C003F36BA /* UITabBarController+RNNOptionsTest.m */,
283
 				E58D26252385888B003F36BA /* UIViewController+LayoutProtocolTest.m */,
290
 				E58D26252385888B003F36BA /* UIViewController+LayoutProtocolTest.m */,
284
 				E58D263E2385888C003F36BA /* UIViewController+RNNOptionsTest.m */,
291
 				E58D263E2385888C003F36BA /* UIViewController+RNNOptionsTest.m */,
292
+				50CF233B240695B10098042D /* RNNBottomTabsController+Helpers.h */,
293
+				50CF233C240695B10098042D /* RNNBottomTabsController+Helpers.m */,
285
 				50647FE223E3196800B92025 /* RNNExternalViewControllerTests.m */,
294
 				50647FE223E3196800B92025 /* RNNExternalViewControllerTests.m */,
286
 				E58D262E2385888B003F36BA /* utils */,
295
 				E58D262E2385888B003F36BA /* utils */,
287
 				E58D261F238587F4003F36BA /* Info.plist */,
296
 				E58D261F238587F4003F36BA /* Info.plist */,
740
 			buildActionMask = 2147483647;
749
 			buildActionMask = 2147483647;
741
 			files = (
750
 			files = (
742
 				5022EDCD2405522000852BA6 /* RNNBottomTabsPresenterTest.m in Sources */,
751
 				5022EDCD2405522000852BA6 /* RNNBottomTabsPresenterTest.m in Sources */,
752
+				50C9A8D1240EB95F00BD699F /* RNNBottomTabsController+Helpers.m in Sources */,
743
 				50996C6923AA487800008F89 /* RNNTestRootViewCreator.m in Sources */,
753
 				50996C6923AA487800008F89 /* RNNTestRootViewCreator.m in Sources */,
744
 				50996C6823AA477400008F89 /* RNNRootViewControllerTest.m in Sources */,
754
 				50996C6823AA477400008F89 /* RNNRootViewControllerTest.m in Sources */,
745
 			);
755
 			);
773
 				E58D26502385888C003F36BA /* RNNNavigationOptionsTest.m in Sources */,
783
 				E58D26502385888C003F36BA /* RNNNavigationOptionsTest.m in Sources */,
774
 				E58D264E2385888C003F36BA /* RNNTestBase.m in Sources */,
784
 				E58D264E2385888C003F36BA /* RNNTestBase.m in Sources */,
775
 				5022EDCE2405524700852BA6 /* RNNBottomTabsAppearancePresenterTest.m in Sources */,
785
 				5022EDCE2405524700852BA6 /* RNNBottomTabsAppearancePresenterTest.m in Sources */,
786
+				500E9FE72406A52100C61231 /* BottomTabPresenterTest.m in Sources */,
776
 				E58D26612385888C003F36BA /* RNNTestNoColor.m in Sources */,
787
 				E58D26612385888C003F36BA /* RNNTestNoColor.m in Sources */,
788
+				50CF233D240695B10098042D /* RNNBottomTabsController+Helpers.m in Sources */,
777
 				E58D265D2385888C003F36BA /* RNNControllerFactoryTest.m in Sources */,
789
 				E58D265D2385888C003F36BA /* RNNControllerFactoryTest.m in Sources */,
778
 				E58D265C2385888C003F36BA /* RNNStackControllerTest.m in Sources */,
790
 				E58D265C2385888C003F36BA /* RNNStackControllerTest.m in Sources */,
779
 				E58D26482385888C003F36BA /* RNNDotIndicatorPresenterTest.m in Sources */,
791
 				E58D26482385888C003F36BA /* RNNDotIndicatorPresenterTest.m in Sources */,