Browse Source

BottomTab dot indicator (#5283)

Add support for dot indicator to BottomTabs.
Guy Carmeli 4 years ago
parent
commit
f42515524d
No account linked to committer's email address
57 changed files with 1208 additions and 490 deletions
  1. 1
    1
      e2e/BottomTabs.test.js
  2. 1
    2
      lib/android/app/build.gradle
  3. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java
  4. 36
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java
  5. 60
    19
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java
  6. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java
  7. 0
    6
      lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java
  8. 4
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabPresenterTest.java
  9. 2
    0
      lib/ios/Bool.h
  10. 5
    0
      lib/ios/Bool.m
  11. 11
    0
      lib/ios/DotIndicatorOptions.h
  12. 28
    0
      lib/ios/DotIndicatorOptions.m
  13. 7
    0
      lib/ios/DotIndicatorParser.h
  14. 10
    0
      lib/ios/DotIndicatorParser.m
  15. 6
    3
      lib/ios/RNNBasePresenter.h
  16. 129
    110
      lib/ios/RNNBasePresenter.m
  17. 18
    15
      lib/ios/RNNBottomTabOptions.h
  18. 24
    34
      lib/ios/RNNBottomTabOptions.m
  19. 13
    16
      lib/ios/RNNControllerFactory.m
  20. 9
    0
      lib/ios/RNNDotIndicatorPresenter.h
  21. 76
    0
      lib/ios/RNNDotIndicatorPresenter.m
  22. 7
    7
      lib/ios/RNNNavigationControllerPresenter.m
  23. 2
    2
      lib/ios/RNNRootViewController.m
  24. 3
    3
      lib/ios/RNNSideMenuPresenter.m
  25. 3
    3
      lib/ios/RNNSplitViewControllerPresenter.m
  26. 4
    0
      lib/ios/RNNTabBarController.m
  27. 0
    1
      lib/ios/RNNTabBarItemCreator.m
  28. 1
    1
      lib/ios/RNNTabBarPresenter.h
  29. 65
    51
      lib/ios/RNNTabBarPresenter.m
  30. 9
    9
      lib/ios/RNNViewControllerPresenter.m
  31. 112
    2
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  32. 26
    26
      lib/ios/ReactNativeNavigationTests/RNNBasePresenterTest.m
  33. 158
    0
      lib/ios/ReactNativeNavigationTests/RNNDotIndicatorPresenterTest.m
  34. 2
    2
      lib/ios/ReactNativeNavigationTests/RNNNavigationControllerPresenterTest.m
  35. 99
    95
      lib/ios/ReactNativeNavigationTests/RNNTabBarControllerTest.m
  36. 54
    33
      lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m
  37. 4
    4
      lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m
  38. 2
    1
      lib/ios/ReactNativeNavigationTests/UITabBarController+RNNOptionsTest.m
  39. 26
    24
      lib/ios/ReactNativeNavigationTests/UIViewController+RNNOptionsTest.m
  40. 9
    0
      lib/ios/ReactNativeNavigationTests/utils/RNNTestBase.h
  41. 19
    0
      lib/ios/ReactNativeNavigationTests/utils/RNNTestBase.m
  42. 10
    0
      lib/ios/UITabBarController+RNNOptions.m
  43. 2
    0
      lib/ios/UIViewController+LayoutProtocol.h
  44. 5
    1
      lib/ios/UIViewController+LayoutProtocol.m
  45. 3
    1
      lib/ios/UIViewController+RNNOptions.h
  46. 12
    8
      lib/ios/UIViewController+RNNOptions.m
  47. 5
    0
      lib/ios/Utils/UIColor+RNNUtils.h
  48. 17
    0
      lib/ios/Utils/UIColor+RNNUtils.m
  49. 8
    0
      lib/ios/Utils/UITabBarController+RNNUtils.h
  50. 21
    0
      lib/ios/Utils/UITabBarController+RNNUtils.m
  51. 6
    0
      lib/ios/Utils/UIView+Utils.h
  52. 12
    0
      lib/ios/Utils/UIView+Utils.m
  53. 6
    0
      lib/ios/Utils/UIViewController+Utils.h
  54. 10
    0
      lib/ios/Utils/UIViewController+Utils.m
  55. 31
    5
      playground/src/screens/FirstBottomTabScreen.js
  56. 8
    1
      playground/src/screens/SecondBottomTabScreen.js
  57. 2
    2
      scripts/test-unit.js

+ 1
- 1
e2e/BottomTabs.test.js View File

@@ -32,7 +32,7 @@ describe('BottomTabs', () => {
32 32
     await expect(element(by.text('NEW'))).toBeVisible();
33 33
   });
34 34
 
35
-  it(':ios: set Tab Bar badge null on a current Tab should reset badge', async () => {
35
+  it('set empty string badge on a current Tab should clear badge', async () => {
36 36
     await elementById(TestIDs.SET_BADGE_BTN).tap();
37 37
     await expect(element(by.text('NEW'))).toBeVisible();
38 38
     await elementById(TestIDs.CLEAR_BADGE_BTN).tap();

+ 1
- 2
lib/android/app/build.gradle View File

@@ -92,12 +92,11 @@ allprojects { p ->
92 92
 def supportLibVersion = safeExtGet('supportLibVersion', DEFAULT_SUPPORT_LIB_VERSION)
93 93
 
94 94
 dependencies {
95
-    implementation fileTree(include: ['*.jar'], dir: 'libs')
96 95
     implementation "com.android.support:design:${supportLibVersion}"
97 96
     implementation "com.android.support:appcompat-v7:${supportLibVersion}"
98 97
     implementation "com.android.support:support-v4:${supportLibVersion}"
99 98
 
100
-    implementation 'com.github.wix-playground:ahbottomnavigation:2.4.9'
99
+    implementation 'com.github.wix-playground:ahbottomnavigation:2.4.15'
101 100
     implementation 'com.github.wix-playground:reflow-animator:1.0.4'
102 101
     implementation 'com.github.clans:fab:1.6.4'
103 102
 

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/BottomTabOptions.java View File

@@ -34,6 +34,7 @@ public class BottomTabOptions {
34 34
         options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
35 35
         options.fontSize = NumberParser.parse(json, "fontSize");
36 36
         options.selectedFontSize = NumberParser.parse(json, "selectedFontSize");
37
+        options.dotIndicator = DotIndicatorOptions.parse(json.optJSONObject("dotIndicator"));
37 38
         return options;
38 39
     }
39 40
 
@@ -46,6 +47,7 @@ public class BottomTabOptions {
46 47
     public Text testId = new NullText();
47 48
     public Text badge = new NullText();
48 49
     public Colour badgeColor = new NullColor();
50
+    public DotIndicatorOptions dotIndicator = new DotIndicatorOptions();
49 51
     public Number fontSize = new NullNumber();
50 52
     public Number selectedFontSize = new NullNumber();
51 53
     @Nullable public Typeface fontFamily;
@@ -64,6 +66,7 @@ public class BottomTabOptions {
64 66
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
65 67
         if (other.selectedFontSize.hasValue()) selectedFontSize = other.selectedFontSize;
66 68
         if (other.fontFamily != null) fontFamily = other.fontFamily;
69
+        if (other.dotIndicator.hasValue()) dotIndicator = other.dotIndicator;
67 70
     }
68 71
 
69 72
     void mergeWithDefault(final BottomTabOptions defaultOptions) {
@@ -79,5 +82,6 @@ public class BottomTabOptions {
79 82
         if (!selectedFontSize.hasValue()) selectedFontSize = defaultOptions.selectedFontSize;
80 83
         if (fontFamily == null) fontFamily = defaultOptions.fontFamily;
81 84
         if (!testId.hasValue()) testId = defaultOptions.testId;
85
+        if (!dotIndicator.hasValue()) dotIndicator = defaultOptions.dotIndicator;
82 86
     }
83 87
 }

+ 36
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/DotIndicatorOptions.java View File

@@ -0,0 +1,36 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import android.support.annotation.Nullable;
4
+
5
+import com.reactnativenavigation.parse.params.Bool;
6
+import com.reactnativenavigation.parse.params.Colour;
7
+import com.reactnativenavigation.parse.params.NullBool;
8
+import com.reactnativenavigation.parse.params.NullColor;
9
+import com.reactnativenavigation.parse.params.NullNumber;
10
+import com.reactnativenavigation.parse.params.Number;
11
+import com.reactnativenavigation.parse.parsers.BoolParser;
12
+import com.reactnativenavigation.parse.parsers.ColorParser;
13
+import com.reactnativenavigation.parse.parsers.NumberParser;
14
+
15
+import org.json.JSONObject;
16
+
17
+public class DotIndicatorOptions {
18
+    public static DotIndicatorOptions parse(@Nullable JSONObject json) {
19
+        DotIndicatorOptions options = new DotIndicatorOptions();
20
+        if (json == null) return options;
21
+
22
+        options.color = ColorParser.parse(json, "color");
23
+        options.size = NumberParser.parse(json, "size");
24
+        options.visible = BoolParser.parse(json, "visible");
25
+
26
+        return options;
27
+    }
28
+
29
+    public Colour color = new NullColor();
30
+    public Number size = new NullNumber();
31
+    public Bool visible = new NullBool();
32
+
33
+    public boolean hasValue() {
34
+        return visible.hasValue();
35
+    }
36
+}

+ 60
- 19
lib/android/app/src/main/java/com/reactnativenavigation/presentation/BottomTabPresenter.java View File

@@ -5,14 +5,20 @@ import android.graphics.drawable.Drawable;
5 5
 import android.support.annotation.NonNull;
6 6
 import android.support.v4.content.ContextCompat;
7 7
 
8
-import com.reactnativenavigation.parse.*;
9
-import com.reactnativenavigation.utils.*;
10
-import com.reactnativenavigation.viewcontrollers.*;
11
-import com.reactnativenavigation.viewcontrollers.bottomtabs.*;
12
-import com.reactnativenavigation.views.*;
8
+import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
9
+import com.reactnativenavigation.parse.BottomTabOptions;
10
+import com.reactnativenavigation.parse.DotIndicatorOptions;
11
+import com.reactnativenavigation.parse.Options;
12
+import com.reactnativenavigation.utils.ImageLoader;
13
+import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;
14
+import com.reactnativenavigation.viewcontrollers.ViewController;
15
+import com.reactnativenavigation.viewcontrollers.bottomtabs.BottomTabFinder;
16
+import com.reactnativenavigation.views.BottomTabs;
13 17
 import com.reactnativenavigation.views.Component;
14 18
 
15
-import java.util.*;
19
+import java.util.List;
20
+
21
+import static com.reactnativenavigation.utils.UiUtils.dpToPx;
16 22
 
17 23
 public class BottomTabPresenter {
18 24
     private final Context context;
@@ -23,6 +29,7 @@ public class BottomTabPresenter {
23 29
     private final int defaultSelectedTextColor;
24 30
     private final int defaultTextColor;
25 31
     private final List<ViewController> tabs;
32
+    private final int defaultDotIndicatorSize;
26 33
 
27 34
     public BottomTabPresenter(Context context, List<ViewController> tabs, ImageLoader imageLoader, Options defaultOptions) {
28 35
         this.tabs = tabs;
@@ -32,6 +39,7 @@ public class BottomTabPresenter {
32 39
         this.defaultOptions = defaultOptions;
33 40
         defaultSelectedTextColor = defaultOptions.bottomTabOptions.selectedIconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationAccent));
34 41
         defaultTextColor = defaultOptions.bottomTabOptions.iconColor.get(ContextCompat.getColor(context, com.aurelhubert.ahbottomnavigation.R.color.colorBottomNavigationInactive));
42
+        defaultDotIndicatorSize = dpToPx(context, 6);
35 43
     }
36 44
 
37 45
     public void setDefaultOptions(Options defaultOptions) {
@@ -45,8 +53,6 @@ public class BottomTabPresenter {
45 53
     public void applyOptions() {
46 54
         for (int i = 0; i < tabs.size(); i++) {
47 55
             BottomTabOptions tab = tabs.get(i).resolveCurrentOptions(defaultOptions).bottomTabOptions;
48
-            bottomTabs.setBadge(i, tab.badge.get(""));
49
-            bottomTabs.setBadgeColor(tab.badgeColor.get(null));
50 56
             bottomTabs.setTitleTypeface(i, tab.fontFamily);
51 57
             bottomTabs.setIconActiveColor(i, tab.selectedIconColor.get(null));
52 58
             bottomTabs.setIconInactiveColor(i, tab.iconColor.get(null));
@@ -55,28 +61,63 @@ public class BottomTabPresenter {
55 61
             bottomTabs.setTitleInactiveTextSizeInSp(i, tab.fontSize.hasValue() ? Float.valueOf(tab.fontSize.get()) : null);
56 62
             bottomTabs.setTitleActiveTextSizeInSp(i, tab.selectedFontSize.hasValue() ? Float.valueOf(tab.selectedFontSize.get()) : null);
57 63
             if (tab.testId.hasValue()) bottomTabs.setTag(i, tab.testId.get());
64
+            if (shouldApplyDot(tab)) applyDotIndicator(i, tab.dotIndicator); else applyBadge(i, tab);
58 65
         }
59 66
     }
60 67
 
61 68
     public void mergeChildOptions(Options options, Component child) {
62 69
         int index = bottomTabFinder.findByComponent(child);
63 70
         if (index >= 0) {
64
-            BottomTabOptions bto = options.bottomTabOptions;
65
-            if (bto.badge.hasValue()) bottomTabs.setBadge(index, bto.badge.get());
66
-            if (bto.badgeColor.hasValue()) bottomTabs.setBadgeColor(bto.badgeColor.get());
67
-            if (bto.fontFamily != null) bottomTabs.setTitleTypeface(index, bto.fontFamily);
68
-            if (bto.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, bto.selectedIconColor.get());
69
-            if (bto.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, bto.iconColor.get());
70
-            if (bto.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, bto.selectedTextColor.get());
71
-            if (bto.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, bto.textColor.get());
72
-            if (bto.text.hasValue()) bottomTabs.setText(index, bto.text.get());
73
-            if (bto.icon.hasValue()) imageLoader.loadIcon(context, bto.icon.get(), new ImageLoadingListenerAdapter() {
71
+            BottomTabOptions tab = options.bottomTabOptions;
72
+            if (tab.fontFamily != null) bottomTabs.setTitleTypeface(index, tab.fontFamily);
73
+            if (tab.selectedIconColor.hasValue()) bottomTabs.setIconActiveColor(index, tab.selectedIconColor.get());
74
+            if (tab.iconColor.hasValue()) bottomTabs.setIconInactiveColor(index, tab.iconColor.get());
75
+            if (tab.selectedTextColor.hasValue()) bottomTabs.setTitleActiveColor(index, tab.selectedTextColor.get());
76
+            if (tab.textColor.hasValue()) bottomTabs.setTitleInactiveColor(index, tab.textColor.get());
77
+            if (tab.text.hasValue()) bottomTabs.setText(index, tab.text.get());
78
+            if (tab.icon.hasValue()) imageLoader.loadIcon(context, tab.icon.get(), new ImageLoadingListenerAdapter() {
74 79
                 @Override
75 80
                 public void onComplete(@NonNull Drawable drawable) {
76 81
                     bottomTabs.setIcon(index, drawable);
77 82
                 }
78 83
             });
79
-            if (bto.testId.hasValue()) bottomTabs.setTag(index, bto.testId.get());
84
+            if (tab.testId.hasValue()) bottomTabs.setTag(index, tab.testId.get());
85
+            if (shouldApplyDot(tab)) mergeDotIndicator(index, tab.dotIndicator); else mergeBadge(index, tab);
80 86
         }
81 87
     }
88
+
89
+    private void applyDotIndicator(int tabIndex, DotIndicatorOptions dotIndicator) {
90
+        AHNotification.Builder builder = new AHNotification.Builder()
91
+                .setText("")
92
+                .setBackgroundColor(dotIndicator.color.get(null))
93
+                .setSize(dotIndicator.size.get(defaultDotIndicatorSize));
94
+        bottomTabs.setNotification(builder.build(), tabIndex);
95
+    }
96
+
97
+    private void applyBadge(int tabIndex, BottomTabOptions tab) {
98
+        AHNotification.Builder builder = new AHNotification.Builder()
99
+                .setText(tab.badge.get(""))
100
+                .setBackgroundColor(tab.badgeColor.get(null));
101
+        bottomTabs.setNotification(builder.build(), tabIndex);
102
+    }
103
+
104
+    private void mergeBadge(int index, BottomTabOptions tab) {
105
+        if (!tab.badge.hasValue()) return;
106
+        AHNotification.Builder builder = new AHNotification.Builder();
107
+        if (tab.badge.hasValue()) builder.setText(tab.badge.get());
108
+        if (tab.badgeColor.hasValue()) builder.setBackgroundColor(tab.badgeColor.get());
109
+        bottomTabs.setNotification(builder.build(), index);
110
+    }
111
+
112
+    private void mergeDotIndicator(int index, DotIndicatorOptions dotIndicator) {
113
+        AHNotification.Builder builder = new AHNotification.Builder();
114
+        if (dotIndicator.color.hasValue()) builder.setBackgroundColor(dotIndicator.color.get());
115
+        builder.setSize(dotIndicator.visible.isFalse() ? 0 : dotIndicator.size.get(defaultDotIndicatorSize));
116
+        AHNotification notification = builder.build();
117
+        if (notification.hasValue()) bottomTabs.setNotification(notification, index);
118
+    }
119
+
120
+    private boolean shouldApplyDot(BottomTabOptions tab) {
121
+        return tab.dotIndicator.visible.hasValue() && !tab.badge.hasValue();
122
+    }
82 123
 }

+ 1
- 0
lib/android/app/src/main/java/com/reactnativenavigation/utils/UiUtils.java View File

@@ -110,6 +110,7 @@ public class UiUtils {
110 110
     }
111 111
 
112 112
     public static int dpToPx(Context context, int dp) {
113
+        if (dp <= 0) return dp;
113 114
         Resources resources = context.getResources();
114 115
         DisplayMetrics metrics = resources.getDisplayMetrics();
115 116
         return (int) (dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));

+ 0
- 6
lib/android/app/src/main/java/com/reactnativenavigation/views/BottomTabs.java View File

@@ -3,7 +3,6 @@ package com.reactnativenavigation.views;
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.graphics.drawable.Drawable;
6
-import android.support.annotation.ColorInt;
7 6
 import android.support.annotation.IntRange;
8 7
 
9 8
 import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
@@ -51,11 +50,6 @@ public class BottomTabs extends AHBottomNavigation {
51 50
         setNotification(badge, bottomTabIndex);
52 51
     }
53 52
 
54
-    public void setBadgeColor(@ColorInt Integer color) {
55
-        if (color == null) return;
56
-        setNotificationBackgroundColor(color);
57
-    }
58
-
59 53
     @Override
60 54
     public void setCurrentItem(@IntRange(from = 0) int position) {
61 55
         super.setCurrentItem(position);

+ 4
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabPresenterTest.java View File

@@ -3,6 +3,7 @@ package com.reactnativenavigation.viewcontrollers;
3 3
 import android.app.Activity;
4 4
 import android.graphics.Color;
5 5
 
6
+import com.aurelhubert.ahbottomnavigation.notification.AHNotification;
6 7
 import com.reactnativenavigation.BaseTest;
7 8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
8 9
 import com.reactnativenavigation.mocks.SimpleViewController;
@@ -19,6 +20,7 @@ import org.mockito.Mockito;
19 20
 import java.util.Arrays;
20 21
 import java.util.List;
21 22
 
23
+import static org.mockito.ArgumentMatchers.any;
22 24
 import static org.mockito.ArgumentMatchers.anyInt;
23 25
 import static org.mockito.ArgumentMatchers.anyString;
24 26
 import static org.mockito.ArgumentMatchers.eq;
@@ -53,7 +55,7 @@ public class BottomTabPresenterTest extends BaseTest {
53 55
     public void present() {
54 56
         uut.applyOptions();
55 57
         for (int i = 0; i < tabs.size(); i++) {
56
-            verify(bottomTabs, times(1)).setBadge(i, tabs.get(i).options.bottomTabOptions.badge.get(""));
58
+            verify(bottomTabs, times(1)).setNotification(any(AHNotification.class), eq(i));
57 59
             verify(bottomTabs, times(1)).setTitleInactiveColor(i, tabs.get(i).options.bottomTabOptions.textColor.get(null));
58 60
             verify(bottomTabs, times(1)).setTitleActiveColor(i, tabs.get(i).options.bottomTabOptions.selectedTextColor.get(null));
59 61
         }
@@ -64,7 +66,7 @@ public class BottomTabPresenterTest extends BaseTest {
64 66
         for (int i = 0; i < 2; i++) {
65 67
             Options options = tabs.get(i).options;
66 68
             uut.mergeChildOptions(options, (Component) tabs.get(i).getView());
67
-            verify(bottomTabs, times(1)).setBadge(i, options.bottomTabOptions.badge.get());
69
+            verify(bottomTabs, times(1)).setNotification(any(AHNotification.class), eq(i));
68 70
             verify(bottomTabs, times(1)).setIconActiveColor(eq(i), anyInt());
69 71
             verify(bottomTabs, times(1)).setIconInactiveColor(eq(i), anyInt());
70 72
         }

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

@@ -10,4 +10,6 @@
10 10
 
11 11
 - (BOOL)getWithDefaultValue:(BOOL)value;
12 12
 
13
+- (bool) isFalse;
14
+
13 15
 @end

+ 5
- 0
lib/ios/Bool.m View File

@@ -29,4 +29,9 @@
29 29
 	}
30 30
 }
31 31
 
32
+- (bool)isFalse {
33
+    return self.value != nil && ![self.value boolValue];
34
+}
35
+
36
+
32 37
 @end

+ 11
- 0
lib/ios/DotIndicatorOptions.h View File

@@ -0,0 +1,11 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface DotIndicatorOptions : RNNOptions
4
+
5
+@property(nonatomic, strong) Color *color;
6
+@property(nonatomic, strong) Number *size;
7
+@property(nonatomic, strong) Bool *visible;
8
+
9
+- (bool)hasValue;
10
+
11
+@end

+ 28
- 0
lib/ios/DotIndicatorOptions.m View File

@@ -0,0 +1,28 @@
1
+#import "DotIndicatorOptions.h"
2
+#import "NullColor.h"
3
+#import "NullNumber.h"
4
+#import "NullBool.h"
5
+
6
+
7
+@implementation DotIndicatorOptions
8
+- (instancetype)initWithDict:(NSDictionary *)dict {
9
+    self = [super init];
10
+
11
+    self.color = [ColorParser parse:dict key:@"color"];
12
+    self.size = [NumberParser parse:dict key:@"size"];
13
+    self.visible = [BoolParser parse:dict key:@"visible"];
14
+    return self;
15
+}
16
+
17
+- (instancetype)init {
18
+    _color = [NullColor new];
19
+    _size = [NullNumber new];
20
+    _visible = [NullBool new];
21
+    return self;
22
+}
23
+
24
+- (bool)hasValue {
25
+    return [self.visible hasValue];
26
+}
27
+
28
+@end

+ 7
- 0
lib/ios/DotIndicatorParser.h View File

@@ -0,0 +1,7 @@
1
+#import <Foundation/Foundation.h>
2
+
3
+@class DotIndicatorOptions;
4
+
5
+@interface DotIndicatorParser : NSObject
6
++ (DotIndicatorOptions *)parse:(NSDictionary *)dict;
7
+@end

+ 10
- 0
lib/ios/DotIndicatorParser.m View File

@@ -0,0 +1,10 @@
1
+#import "DotIndicatorParser.h"
2
+#import "DotIndicatorOptions.h"
3
+
4
+
5
+@implementation DotIndicatorParser
6
++ (DotIndicatorOptions *)parse:(NSDictionary *)dict {
7
+    return [[DotIndicatorOptions alloc] initWithDict:dict[@"dotIndicator"]];
8
+}
9
+
10
+@end

+ 6
- 3
lib/ios/RNNBasePresenter.h View File

@@ -4,11 +4,11 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
4 4
 
5 5
 @interface RNNBasePresenter : NSObject
6 6
 
7
-@property (nonatomic, weak) id bindedViewController;
7
+@property(nonatomic, weak) id boundViewController;
8 8
 
9
-@property (nonatomic, strong) NSString* bindedComponentId;
9
+@property(nonatomic, strong) NSString *boundComponentId;
10 10
 
11
-- (void)bindViewController:(UIViewController *)bindedViewController;
11
+- (void)bindViewController:(UIViewController *)boundViewController;
12 12
 
13 13
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions;
14 14
 
@@ -18,8 +18,11 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
18 18
 
19 19
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options;
20 20
 
21
+- (void)applyDotIndicator:(UIViewController *)child;
22
+
21 23
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions;
22 24
 
23 25
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
24 26
 
27
+- (void)viewDidLayoutSubviews;
25 28
 @end

+ 129
- 110
lib/ios/RNNBasePresenter.m View File

@@ -3,140 +3,159 @@
3 3
 #import "RNNTabBarItemCreator.h"
4 4
 #import "RNNReactComponentRegistry.h"
5 5
 #import "UIViewController+LayoutProtocol.h"
6
+#import "DotIndicatorOptions.h"
7
+#import "RNNDotIndicatorPresenter.h"
6 8
 
7 9
 @interface RNNBasePresenter ()
10
+@property(nonatomic, strong) RNNDotIndicatorPresenter* dotIndicatorPresenter;
8 11
 @end
9 12
 
10
-
11 13
 @implementation RNNBasePresenter
12 14
 
13
-- (void)bindViewController:(UIViewController<RNNLayoutProtocol> *)bindedViewController {
14
-	self.bindedComponentId = bindedViewController.layoutInfo.componentId;
15
-	_bindedViewController = bindedViewController;
15
+-(instancetype)init {
16
+    self = [super init];
17
+    self.dotIndicatorPresenter = [RNNDotIndicatorPresenter new];
18
+    return self;
19
+}
20
+
21
+- (void)bindViewController:(UIViewController <RNNLayoutProtocol> *)boundViewController {
22
+    self.boundComponentId = boundViewController.layoutInfo.componentId;
23
+    _boundViewController = boundViewController;
16 24
 }
17 25
 
18 26
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions {
19
-	
27
+
20 28
 }
21 29
 
22 30
 - (void)applyOptionsOnViewDidLayoutSubviews:(RNNNavigationOptions *)options {
23
-	
31
+
24 32
 }
25 33
 
26 34
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
27
-	UIViewController* viewController = self.bindedViewController;
28
-	
29
-	if (options.bottomTab.text.hasValue) {
30
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
31
-		viewController.tabBarItem = tabItem;
32
-	}
33
-	
34
-	if (options.bottomTab.icon.hasValue) {
35
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
36
-		viewController.tabBarItem = tabItem;
37
-	}
38
-	
39
-	if (options.bottomTab.selectedIcon.hasValue) {
40
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
41
-		viewController.tabBarItem = tabItem;
42
-	}
43
-	
44
-	if (options.bottomTab.badgeColor.hasValue) {
45
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
46
-		viewController.tabBarItem = tabItem;
47
-	}
48
-	
49
-	if (options.bottomTab.textColor.hasValue) {
50
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
51
-		viewController.tabBarItem = tabItem;
52
-	}
53
-	
54
-	if (options.bottomTab.iconColor.hasValue) {
55
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
56
-		viewController.tabBarItem = tabItem;
57
-	}
58
-	
59
-	if (options.bottomTab.selectedTextColor.hasValue) {
60
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
61
-		viewController.tabBarItem = tabItem;
62
-	}
63
-	
64
-	if (options.bottomTab.selectedIconColor.hasValue) {
65
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
66
-		viewController.tabBarItem = tabItem;
67
-	}
35
+    UIViewController *viewController = self.boundViewController;
36
+
37
+    if (options.bottomTab.text.hasValue) {
38
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
39
+        viewController.tabBarItem = tabItem;
40
+    }
41
+
42
+    if (options.bottomTab.icon.hasValue) {
43
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
44
+        viewController.tabBarItem = tabItem;
45
+    }
46
+
47
+    if (options.bottomTab.selectedIcon.hasValue) {
48
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
49
+        viewController.tabBarItem = tabItem;
50
+    }
51
+
52
+    if (options.bottomTab.badgeColor.hasValue) {
53
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
54
+        viewController.tabBarItem = tabItem;
55
+    }
56
+
57
+    if (options.bottomTab.textColor.hasValue) {
58
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
59
+        viewController.tabBarItem = tabItem;
60
+    }
61
+
62
+    if (options.bottomTab.iconColor.hasValue) {
63
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
64
+        viewController.tabBarItem = tabItem;
65
+    }
66
+
67
+    if (options.bottomTab.selectedTextColor.hasValue) {
68
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
69
+        viewController.tabBarItem = tabItem;
70
+    }
71
+
72
+    if (options.bottomTab.selectedIconColor.hasValue) {
73
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:options.bottomTab];
74
+        viewController.tabBarItem = tabItem;
75
+    }
68 76
 }
69 77
 
70 78
 - (void)applyOptions:(RNNNavigationOptions *)options {
71
-	UIViewController* viewController = self.bindedViewController;
72
-	
73
-	if (options.bottomTab.badge.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
74
-		[viewController rnn_setTabBarItemBadge:options.bottomTab.badge.get];
75
-	}
76
-	
77
-	if (options.bottomTab.badgeColor.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
78
-		[viewController rnn_setTabBarItemBadgeColor:options.bottomTab.badgeColor.get];
79
-	}
79
+    UIViewController *viewController = self.boundViewController;
80
+
81
+    if (options.bottomTab.badge.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
82
+        [viewController rnn_setTabBarItemBadge:options.bottomTab];
83
+    }
84
+
85
+    if (options.bottomTab.badgeColor.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
86
+        [viewController rnn_setTabBarItemBadgeColor:options.bottomTab.badgeColor.get];
87
+    }
80 88
 }
81 89
 
82 90
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
83
-	UIViewController* viewController = self.bindedViewController;
84
-	if (newOptions.bottomTab.badge.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
85
-		[viewController rnn_setTabBarItemBadge:newOptions.bottomTab.badge.get];
86
-	}
87
-	
88
-	if (newOptions.bottomTab.badgeColor.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
89
-		[viewController rnn_setTabBarItemBadgeColor:newOptions.bottomTab.badgeColor.get];
90
-	}
91
-	
92
-	if (newOptions.bottomTab.text.hasValue) {
93
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
94
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
95
-		viewController.tabBarItem = tabItem;
96
-	}
97
-	
98
-	if (newOptions.bottomTab.icon.hasValue) {
99
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
100
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
101
-		viewController.tabBarItem = tabItem;
102
-	}
103
-	
104
-	if (newOptions.bottomTab.selectedIcon.hasValue) {
105
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
106
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
107
-		viewController.tabBarItem = tabItem;
108
-	}
109
-	
110
-	if (newOptions.bottomTab.textColor.hasValue) {
111
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
112
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
113
-		viewController.tabBarItem = tabItem;
114
-	}
115
-	
116
-	if (newOptions.bottomTab.selectedTextColor.hasValue) {
117
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
118
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
119
-		viewController.tabBarItem = tabItem;
120
-	}
121
-	
122
-	if (newOptions.bottomTab.iconColor.hasValue) {
123
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
124
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
125
-		viewController.tabBarItem = tabItem;
126
-	}
127
-	
128
-	if (newOptions.bottomTab.selectedIconColor.hasValue) {
129
-		RNNNavigationOptions* buttonsResolvedOptions = [(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
130
-		UITabBarItem* tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
131
-		viewController.tabBarItem = tabItem;
132
-	}
91
+    UIViewController *viewController = self.boundViewController;
92
+    if (newOptions.bottomTab.badge.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
93
+        [viewController rnn_setTabBarItemBadge:newOptions.bottomTab];
94
+    }
95
+
96
+    if (newOptions.bottomTab.badgeColor.hasValue && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
97
+        [viewController rnn_setTabBarItemBadgeColor:newOptions.bottomTab.badgeColor.get];
98
+    }
99
+
100
+    if ([newOptions.bottomTab.dotIndicator hasValue] && [viewController.parentViewController isKindOfClass:[UITabBarController class]]) {
101
+        [[self dotIndicatorPresenter] apply:viewController:newOptions.bottomTab.dotIndicator];
102
+    }
103
+
104
+    if (newOptions.bottomTab.text.hasValue) {
105
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
106
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
107
+        viewController.tabBarItem = tabItem;
108
+    }
109
+
110
+    if (newOptions.bottomTab.icon.hasValue) {
111
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
112
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
113
+        viewController.tabBarItem = tabItem;
114
+    }
115
+
116
+    if (newOptions.bottomTab.selectedIcon.hasValue) {
117
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
118
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
119
+        viewController.tabBarItem = tabItem;
120
+    }
121
+
122
+    if (newOptions.bottomTab.textColor.hasValue) {
123
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
124
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
125
+        viewController.tabBarItem = tabItem;
126
+    }
127
+
128
+    if (newOptions.bottomTab.selectedTextColor.hasValue) {
129
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
130
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
131
+        viewController.tabBarItem = tabItem;
132
+    }
133
+
134
+    if (newOptions.bottomTab.iconColor.hasValue) {
135
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
136
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
137
+        viewController.tabBarItem = tabItem;
138
+    }
139
+
140
+    if (newOptions.bottomTab.selectedIconColor.hasValue) {
141
+        RNNNavigationOptions *buttonsResolvedOptions = [(RNNNavigationOptions *) [currentOptions overrideOptions:newOptions] withDefault:defaultOptions];
142
+        UITabBarItem *tabItem = [RNNTabBarItemCreator updateTabBarItem:viewController.tabBarItem bottomTabOptions:buttonsResolvedOptions.bottomTab];
143
+        viewController.tabBarItem = tabItem;
144
+    }
133 145
 }
134 146
 
135 147
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
136
-	if (readyBlock) {
137
-		readyBlock();
138
-		readyBlock = nil;
139
-	}
148
+    if (readyBlock) {
149
+        readyBlock();
150
+        readyBlock = nil;
151
+    }
140 152
 }
141 153
 
154
+- (void)viewDidLayoutSubviews {
155
+
156
+}
157
+
158
+- (void)applyDotIndicator:(UIViewController *)child {
159
+    [[self dotIndicatorPresenter] apply:child:[child resolveOptions].bottomTab.dotIndicator];
160
+}
142 161
 @end

+ 18
- 15
lib/ios/RNNBottomTabOptions.h View File

@@ -1,22 +1,25 @@
1 1
 #import "RNNOptions.h"
2 2
 
3
+@class DotIndicatorOptions;
4
+
3 5
 @interface RNNBottomTabOptions : RNNOptions
4 6
 
5
-@property (nonatomic) NSUInteger tag;
6
-@property (nonatomic, strong) Text* text;
7
-@property (nonatomic, strong) Text* badge;
8
-@property (nonatomic, strong) Text* fontFamily;
9
-@property (nonatomic, strong) Text* testID;
10
-@property (nonatomic, strong) Color* badgeColor;
11
-@property (nonatomic, strong) Image* icon;
12
-@property (nonatomic, strong) Image* selectedIcon;
13
-@property (nonatomic, strong) Color* iconColor;
14
-@property (nonatomic, strong) Color* selectedIconColor;
15
-@property (nonatomic, strong) Color* selectedTextColor;
16
-@property (nonatomic, strong) Dictionary* iconInsets;
17
-@property (nonatomic, strong) Color* textColor;
18
-@property (nonatomic, strong) Number* fontSize;
19
-@property (nonatomic, strong) Bool* visible;
7
+@property(nonatomic) NSUInteger tag;
8
+@property(nonatomic, strong) Text *text;
9
+@property(nonatomic, strong) Text *badge;
10
+@property(nonatomic, strong) Color *badgeColor;
11
+@property(nonatomic, strong) DotIndicatorOptions *dotIndicator;
12
+@property(nonatomic, strong) Text *fontFamily;
13
+@property(nonatomic, strong) Text *testID;
14
+@property(nonatomic, strong) Image *icon;
15
+@property(nonatomic, strong) Image *selectedIcon;
16
+@property(nonatomic, strong) Color *iconColor;
17
+@property(nonatomic, strong) Color *selectedIconColor;
18
+@property(nonatomic, strong) Color *selectedTextColor;
19
+@property(nonatomic, strong) Dictionary *iconInsets;
20
+@property(nonatomic, strong) Color *textColor;
21
+@property(nonatomic, strong) Number *fontSize;
22
+@property(nonatomic, strong) Bool *visible;
20 23
 
21 24
 
22 25
 @end

+ 24
- 34
lib/ios/RNNBottomTabOptions.m View File

@@ -1,43 +1,33 @@
1 1
 #import "RNNBottomTabOptions.h"
2
-#import "UIImage+tint.h"
3
-#import "UITabBarController+RNNOptions.h"
4
-#import "UIViewController+RNNOptions.h"
5
-#import "RNNTabBarItemCreator.h"
2
+#import "DotIndicatorOptions.h"
3
+#import "DotIndicatorParser.h"
6 4
 
7 5
 @implementation RNNBottomTabOptions
8 6
 
9 7
 - (instancetype)initWithDict:(NSDictionary *)dict {
10
-	self = [super init];
11
-	
12
-	self.text = [TextParser parse:dict key:@"text"];
13
-	self.badge = [TextParser parse:dict key:@"badge"];
14
-	self.fontFamily = [TextParser parse:dict key:@"fontFamily"];
15
-	self.testID = [TextParser parse:dict key:@"testID"];
16
-	
17
-	
18
-	self.badgeColor = [ColorParser parse:dict key:@"badgeColor"];
19
-	self.icon = [ImageParser parse:dict key:@"icon"];
20
-	self.selectedIcon = [ImageParser parse:dict key:@"selectedIcon"];
21
-	self.iconColor = [ColorParser parse:dict key:@"iconColor"];
22
-	self.selectedIconColor = [ColorParser parse:dict key:@"selectedIconColor"];
23
-	self.selectedTextColor = [ColorParser parse:dict key:@"selectedTextColor"];
24
-	self.iconInsets = [DictionaryParser parse:dict key:@"iconInsets"];
25
-	
26
-	self.textColor = [ColorParser parse:dict key:@"textColor"];
27
-	self.fontSize = [NumberParser parse:dict key:@"fontSize"];
28
-	self.visible = [BoolParser parse:dict key:@"visible"];
29
-	
30
-	return self;
31
-}
8
+    self = [super init];
9
+    self.tag = arc4random();
10
+
11
+    self.text = [TextParser parse:dict key:@"text"];
12
+    self.badge = [TextParser parse:dict key:@"badge"];
13
+    self.badgeColor = [ColorParser parse:dict key:@"badgeColor"];
14
+    self.fontFamily = [TextParser parse:dict key:@"fontFamily"];
15
+    self.testID = [TextParser parse:dict key:@"testID"];
16
+
17
+    self.dotIndicator = [DotIndicatorParser parse:dict];
18
+
19
+    self.icon = [ImageParser parse:dict key:@"icon"];
20
+    self.selectedIcon = [ImageParser parse:dict key:@"selectedIcon"];
21
+    self.iconColor = [ColorParser parse:dict key:@"iconColor"];
22
+    self.selectedIconColor = [ColorParser parse:dict key:@"selectedIconColor"];
23
+    self.selectedTextColor = [ColorParser parse:dict key:@"selectedTextColor"];
24
+    self.iconInsets = [DictionaryParser parse:dict key:@"iconInsets"];
25
+
26
+    self.textColor = [ColorParser parse:dict key:@"textColor"];
27
+    self.fontSize = [NumberParser parse:dict key:@"fontSize"];
28
+    self.visible = [BoolParser parse:dict key:@"visible"];
32 29
 
33
--(void)resetOptions {
34
-	self.text = nil;
35
-	self.badge = nil;
36
-	self.visible = nil;
37
-	self.icon = nil;
38
-	self.testID = nil;
39
-	self.iconInsets = nil;
40
-	self.selectedIcon = nil;
30
+    return self;
41 31
 }
42 32
 
43 33
 @end

+ 13
- 16
lib/ios/RNNControllerFactory.m View File

@@ -1,20 +1,10 @@
1 1
 #import "RNNControllerFactory.h"
2
-#import "RNNLayoutNode.h"
3 2
 #import "RNNSplitViewController.h"
4
-#import "RNNSplitViewOptions.h"
5 3
 #import "RNNSideMenuController.h"
6
-#import "RNNSideMenuChildVC.h"
7 4
 #import "RNNNavigationController.h"
8 5
 #import "RNNTabBarController.h"
9 6
 #import "RNNTopTabsViewController.h"
10
-#import "RNNLayoutInfo.h"
11 7
 #import "RNNRootViewController.h"
12
-#import "UIViewController+SideMenuController.h"
13
-#import "RNNViewControllerPresenter.h"
14
-#import "RNNNavigationControllerPresenter.h"
15
-#import "RNNTabBarPresenter.h"
16
-#import "RNNSideMenuPresenter.h"
17
-#import "RNNSplitViewControllerPresenter.h"
18 8
 
19 9
 @implementation RNNControllerFactory {
20 10
 	id<RNNRootViewCreator> _creator;
@@ -72,7 +62,7 @@
72 62
 	}
73 63
 	
74 64
 	else if (node.isTabs) {
75
-		result = [self createTabs:node];
65
+		result = [self createBottomTabs:node];
76 66
 	}
77 67
 	
78 68
 	else if (node.isTopTabs) {
@@ -117,7 +107,7 @@
117 107
 	
118 108
 	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter presenter:presenter options:options defaultOptions:_defaultOptions];
119 109
 	
120
-	return (UIViewController *)component;
110
+	return component;
121 111
 }
122 112
 
123 113
 - (UIViewController *)createExternalComponent:(RNNLayoutNode*)node {
@@ -130,7 +120,7 @@
130 120
 	RNNRootViewController* component = [[RNNRootViewController alloc] initExternalComponentWithLayoutInfo:layoutInfo eventEmitter:_eventEmitter presenter:presenter options:options defaultOptions:_defaultOptions];
131 121
 	[component bindViewController:externalVC];
132 122
 	
133
-	return (UIViewController *)component;
123
+	return component;
134 124
 }
135 125
 
136 126
 
@@ -146,14 +136,21 @@
146 136
 	return stack;
147 137
 }
148 138
 
149
--(UIViewController *)createTabs:(RNNLayoutNode*)node {
139
+-(UIViewController *)createBottomTabs:(RNNLayoutNode*)node {
150 140
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
151 141
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];;
152
-	RNNTabBarPresenter* presenter = [[RNNTabBarPresenter alloc] init];
142
+	RNNTabBarPresenter* presenter = [RNNTabBarPresenter new];
153 143
 
154 144
 	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
155 145
 	
156
-	RNNTabBarController* tabsController = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo creator:_creator options:options defaultOptions:_defaultOptions presenter:presenter eventEmitter:_eventEmitter childViewControllers:childViewControllers];
146
+	RNNTabBarController* tabsController = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo
147
+            creator:_creator
148
+            options:options
149
+			defaultOptions:_defaultOptions
150
+			presenter:presenter
151
+			eventEmitter:_eventEmitter
152
+			childViewControllers:childViewControllers
153
+	];
157 154
 	
158 155
 	return tabsController;
159 156
 }

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

@@ -0,0 +1,9 @@
1
+#import <Foundation/Foundation.h>
2
+
3
+@class UIViewController;
4
+@class DotIndicatorOptions;
5
+
6
+
7
+@interface RNNDotIndicatorPresenter : NSObject
8
+- (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options;
9
+@end

+ 76
- 0
lib/ios/RNNDotIndicatorPresenter.m View File

@@ -0,0 +1,76 @@
1
+#import <UIKit/UIKit.h>
2
+#import "RNNDotIndicatorPresenter.h"
3
+#import "UIViewController+LayoutProtocol.h"
4
+#import "DotIndicatorOptions.h"
5
+#import "UITabBarController+RNNUtils.h"
6
+
7
+@implementation RNNDotIndicatorPresenter
8
+
9
+- (void)apply:(UIViewController *)child :(DotIndicatorOptions *)options {
10
+    if (![options hasValue]) return;
11
+
12
+    if ([options.visible isFalse]) {
13
+        if ([child tabBarItem].tag > 0) [self remove:child];
14
+        return;
15
+    }
16
+    if ([self currentIndicatorEquals:child :options]) return;
17
+
18
+    if ([self hasIndicator:child]) [self remove:child];
19
+
20
+    UIView *indicator = [self createIndicator:options];
21
+    [child tabBarItem].tag = indicator.tag;
22
+
23
+    UITabBarController *bottomTabs = [self getTabBarController:child];
24
+    int index = (int) [[bottomTabs childViewControllers] indexOfObject:child];
25
+    [[bottomTabs getTabView:index] addSubview:indicator];
26
+    [self applyConstraints:options badge:indicator tabBar:bottomTabs index:index];
27
+}
28
+
29
+- (UIView *)createIndicator:(DotIndicatorOptions *)options {
30
+    UIView * indicator = [UIView new];
31
+    indicator.translatesAutoresizingMaskIntoConstraints = NO;
32
+    indicator.layer.cornerRadius = [[options.size getWithDefaultValue:@6] floatValue] / 2;
33
+    indicator.backgroundColor = [options.color getWithDefaultValue:[UIColor redColor]];
34
+    indicator.tag = arc4random();
35
+    return indicator;
36
+}
37
+
38
+
39
+- (void)applyConstraints:(DotIndicatorOptions *)options badge:(UIView *)badge tabBar:(UITabBarController *)bottomTabs index:(int)index {
40
+    UIView *icon = [bottomTabs getTabIcon:index];
41
+    float size = [[options.size getWithDefaultValue:@6] floatValue];
42
+    [NSLayoutConstraint activateConstraints:@[
43
+            [badge.leftAnchor constraintEqualToAnchor:icon.rightAnchor constant:-size / 2],
44
+            [badge.topAnchor constraintEqualToAnchor:icon.topAnchor constant:-size / 2],
45
+            [badge.widthAnchor constraintEqualToConstant:size],
46
+            [badge.heightAnchor constraintEqualToConstant:size]
47
+    ]];
48
+}
49
+
50
+- (BOOL)currentIndicatorEquals:(UIViewController *)child :(DotIndicatorOptions *)options {
51
+    if (![self hasIndicator:child]) return NO;
52
+    UIView *currentIndicator = [self getCurrentIndicator:child];
53
+    return [[currentIndicator backgroundColor] isEqual:[options.color getWithDefaultValue:[UIColor redColor]]];
54
+}
55
+
56
+- (UIView *)getCurrentIndicator:(UIViewController *)child {
57
+    UITabBarController *bottomTabs = [self getTabBarController:child];
58
+    int tabIndex = (int) [[bottomTabs childViewControllers] indexOfObject:child];
59
+    return [[bottomTabs getTabView:tabIndex] viewWithTag:[child tabBarItem].tag];
60
+}
61
+
62
+- (BOOL)hasIndicator:(UIViewController *)child {
63
+    return [child tabBarItem].tag > 0;
64
+}
65
+
66
+- (void)remove:(UIViewController *)child {
67
+    UIView *view = [[[child tabBarController] tabBar] viewWithTag:[child tabBarItem].tag];
68
+    [view removeFromSuperview];
69
+    [child tabBarItem].tag = -1;
70
+}
71
+
72
+- (UITabBarController *)getTabBarController:(id)viewController {
73
+    return [viewController isKindOfClass:[UITabBarController class]] ? viewController : [viewController tabBarController];
74
+}
75
+
76
+@end

+ 7
- 7
lib/ios/RNNNavigationControllerPresenter.m View File

@@ -24,7 +24,7 @@
24 24
 - (void)applyOptions:(RNNNavigationOptions *)options {
25 25
 	[super applyOptions:options];
26 26
 	
27
-	RNNNavigationController* navigationController = self.bindedViewController;
27
+	RNNNavigationController* navigationController = self.boundViewController;
28 28
 	
29 29
 	self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new];
30 30
 	self.interactivePopGestureDelegate.navigationController = navigationController;
@@ -59,7 +59,7 @@
59 59
 }
60 60
 
61 61
 - (void)applyOptionsBeforePopping:(RNNNavigationOptions *)options {
62
-	RNNNavigationController* navigationController = self.bindedViewController;
62
+	RNNNavigationController* navigationController = self.boundViewController;
63 63
 	[navigationController setTopBarBackgroundColor:[options.topBar.background.color getWithDefaultValue:nil]];
64 64
 	[navigationController rnn_setNavigationBarFontFamily:[options.topBar.title.fontFamily getWithDefaultValue:nil] fontSize:[options.topBar.title.fontSize getWithDefaultValue:@(17)] color:[options.topBar.title.color getWithDefaultValue:[UIColor blackColor]]];
65 65
 	[navigationController rnn_setNavigationBarLargeTitleVisible:[options.topBar.largeTitle.visible getWithDefaultValue:NO]];
@@ -68,7 +68,7 @@
68 68
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
69 69
 	[super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
70 70
 	
71
-	RNNNavigationController* navigationController = self.bindedViewController;
71
+	RNNNavigationController* navigationController = self.boundViewController;
72 72
 	
73 73
 	if (newOptions.popGesture.hasValue) {
74 74
 		[navigationController rnn_setInteractivePopGestureEnabled:newOptions.popGesture.get];
@@ -168,7 +168,7 @@
168 168
 }
169 169
 
170 170
 - (void)setCustomNavigationBarView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
171
-	RNNNavigationController* navigationController = self.bindedViewController;
171
+	RNNNavigationController* navigationController = self.boundViewController;
172 172
 	if (![options.topBar.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
173 173
 		readyBlock();
174 174
 		readyBlock = nil;
@@ -194,7 +194,7 @@
194 194
 }
195 195
 
196 196
 - (void)setCustomNavigationComponentBackground:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
197
-	RNNNavigationController* navigationController = self.bindedViewController;
197
+	RNNNavigationController* navigationController = self.boundViewController;
198 198
 	if (![options.topBar.background.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
199 199
 		readyBlock();
200 200
 		readyBlock = nil;
@@ -214,7 +214,7 @@
214 214
 }
215 215
 
216 216
 - (void)presentBackgroundComponent {
217
-	RNNNavigationController* navigationController = self.bindedViewController;
217
+	RNNNavigationController* navigationController = self.boundViewController;
218 218
 	if (_customTopBarBackground) {
219 219
 		[_customTopBarBackground removeFromSuperview];
220 220
 	}
@@ -225,7 +225,7 @@
225 225
 }
226 226
 
227 227
 - (void)dealloc {
228
-	[_componentRegistry removeComponent:self.bindedComponentId];
228
+	[_componentRegistry removeComponent:self.boundComponentId];
229 229
 }
230 230
 
231 231
 @end

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

@@ -29,7 +29,7 @@
29 29
 
30 30
 - (void)mergeOptions:(RNNNavigationOptions *)options {
31 31
 	[_presenter mergeOptions:options currentOptions:self.options defaultOptions:self.defaultOptions];
32
-	[((UIViewController<RNNLayoutProtocol> *)self.parentViewController) mergeOptions:options];
32
+	[self.parentViewController mergeOptions:options];
33 33
 }
34 34
 
35 35
 - (void)overrideOptions:(RNNNavigationOptions *)options {
@@ -42,7 +42,7 @@
42 42
 	[_presenter applyOptions:self.resolveOptions];
43 43
 	[_presenter renderComponents:self.resolveOptions perform:nil];
44 44
 	
45
-	[((UIViewController *)self.parentViewController) onChildWillAppear];
45
+	[self.parentViewController onChildWillAppear];
46 46
 }
47 47
 
48 48
 -(void)viewDidAppear:(BOOL)animated {

+ 3
- 3
lib/ios/RNNSideMenuPresenter.m View File

@@ -6,7 +6,7 @@
6 6
 - (void)applyOptions:(RNNNavigationOptions *)options {
7 7
 	[super applyOptions:options];
8 8
 		
9
-	RNNSideMenuController* sideMenuController = self.bindedViewController;
9
+	RNNSideMenuController* sideMenuController = self.boundViewController;
10 10
 	
11 11
 	[sideMenuController side:MMDrawerSideLeft enabled:[options.sideMenu.left.enabled getWithDefaultValue:YES]];
12 12
 	[sideMenuController side:MMDrawerSideRight enabled:[options.sideMenu.right.enabled getWithDefaultValue:YES]];
@@ -41,7 +41,7 @@
41 41
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions {
42 42
 	[super applyOptionsOnInit:initialOptions];
43 43
 	
44
-	RNNSideMenuController* sideMenuController = self.bindedViewController;
44
+	RNNSideMenuController* sideMenuController = self.boundViewController;
45 45
 	if (initialOptions.sideMenu.left.width.hasValue) {
46 46
 		[sideMenuController side:MMDrawerSideLeft width:initialOptions.sideMenu.left.width.get];
47 47
 	}
@@ -56,7 +56,7 @@
56 56
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
57 57
 	[super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
58 58
 	
59
-	RNNSideMenuController* sideMenuController = self.bindedViewController;
59
+	RNNSideMenuController* sideMenuController = self.boundViewController;
60 60
 	
61 61
 	if (newOptions.sideMenu.left.enabled.hasValue) {
62 62
 		[sideMenuController side:MMDrawerSideLeft enabled:newOptions.sideMenu.left.enabled.get];

+ 3
- 3
lib/ios/RNNSplitViewControllerPresenter.m View File

@@ -8,7 +8,7 @@
8 8
 - (void)applyOptions:(RNNNavigationOptions *)options {
9 9
 	[super applyOptions:options];
10 10
 	
11
-	UISplitViewController* splitViewController = self.bindedViewController;
11
+	UISplitViewController* splitViewController = self.boundViewController;
12 12
 	[splitViewController rnn_setDisplayMode:options.splitView.displayMode];
13 13
 	[splitViewController rnn_setPrimaryEdge:options.splitView.primaryEdge];
14 14
 	[splitViewController rnn_setMinWidth:options.splitView.minWidth];
@@ -19,7 +19,7 @@
19 19
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions {
20 20
 	[super applyOptionsOnInit:initialOptions];
21 21
 	
22
-	UISplitViewController* splitViewController = self.bindedViewController;
22
+	UISplitViewController* splitViewController = self.boundViewController;
23 23
 	[splitViewController rnn_setDisplayMode:initialOptions.splitView.displayMode];
24 24
 	[splitViewController rnn_setPrimaryEdge:initialOptions.splitView.primaryEdge];
25 25
 	[splitViewController rnn_setMinWidth:initialOptions.splitView.minWidth];
@@ -29,7 +29,7 @@
29 29
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
30 30
 	[super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
31 31
 	
32
-	UISplitViewController* splitViewController = self.bindedViewController;
32
+	UISplitViewController* splitViewController = self.boundViewController;
33 33
 
34 34
 	if (newOptions.splitView.displayMode) {
35 35
 		[splitViewController rnn_setDisplayMode:newOptions.splitView.displayMode];

+ 4
- 0
lib/ios/RNNTabBarController.m View File

@@ -8,6 +8,10 @@
8 8
 	return self;
9 9
 }
10 10
 
11
+- (void)viewDidLayoutSubviews {
12
+	[self.presenter viewDidLayoutSubviews];
13
+}
14
+
11 15
 - (UIViewController *)getCurrentChild {
12 16
 	return self.selectedViewController;
13 17
 }

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

@@ -1,5 +1,4 @@
1 1
 #import "RNNTabBarItemCreator.h"
2
-#import <React/RCTConvert.h>
3 2
 #import "UIImage+tint.h"
4 3
 
5 4
 @implementation RNNTabBarItemCreator

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

@@ -1,5 +1,5 @@
1 1
 #import "RNNBasePresenter.h"
2 2
 
3 3
 @interface RNNTabBarPresenter : RNNBasePresenter
4
-
4
+- (void)applyDotIndicator;
5 5
 @end

+ 65
- 51
lib/ios/RNNTabBarPresenter.m View File

@@ -1,66 +1,80 @@
1 1
 #import "RNNTabBarPresenter.h"
2 2
 #import "UITabBarController+RNNOptions.h"
3
+#import "UIViewController+LayoutProtocol.h"
4
+#import "UIViewController+Utils.h"
3 5
 
4 6
 @implementation RNNTabBarPresenter
5 7
 
6 8
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
7
-	UITabBarController* tabBarController = self.bindedViewController;
8
-	[tabBarController rnn_setCurrentTabIndex:[options.bottomTabs.currentTabIndex getWithDefaultValue:0]];
9
+    UITabBarController *tabBarController = self.boundViewController;
10
+    [tabBarController rnn_setCurrentTabIndex:[options.bottomTabs.currentTabIndex getWithDefaultValue:0]];
9 11
 }
10 12
 
11 13
 - (void)applyOptions:(RNNNavigationOptions *)options {
12
-	UITabBarController* tabBarController = self.bindedViewController;
13
-	
14
-	[tabBarController rnn_setTabBarTestID:[options.bottomTabs.testID getWithDefaultValue:nil]];
15
-	[tabBarController rnn_setTabBarBackgroundColor:[options.bottomTabs.backgroundColor getWithDefaultValue:nil]];
16
-	[tabBarController rnn_setTabBarTranslucent:[options.bottomTabs.translucent getWithDefaultValue:NO]];
17
-	[tabBarController rnn_setTabBarHideShadow:[options.bottomTabs.hideShadow getWithDefaultValue:NO]];
18
-	[tabBarController rnn_setTabBarStyle:[RCTConvert UIBarStyle:[options.bottomTabs.barStyle getWithDefaultValue:@"default"]]];
19
-	[tabBarController rnn_setTabBarVisible:[options.bottomTabs.visible getWithDefaultValue:YES] animated:[options.bottomTabs.animate getWithDefaultValue:NO]];
14
+    UITabBarController *tabBarController = self.boundViewController;
15
+
16
+    [tabBarController rnn_setTabBarTestID:[options.bottomTabs.testID getWithDefaultValue:nil]];
17
+    [tabBarController rnn_setTabBarBackgroundColor:[options.bottomTabs.backgroundColor getWithDefaultValue:nil]];
18
+    [tabBarController rnn_setTabBarTranslucent:[options.bottomTabs.translucent getWithDefaultValue:NO]];
19
+    [tabBarController rnn_setTabBarHideShadow:[options.bottomTabs.hideShadow getWithDefaultValue:NO]];
20
+    [tabBarController rnn_setTabBarStyle:[RCTConvert UIBarStyle:[options.bottomTabs.barStyle getWithDefaultValue:@"default"]]];
21
+    [tabBarController rnn_setTabBarVisible:[options.bottomTabs.visible getWithDefaultValue:YES] animated:[options.bottomTabs.animate getWithDefaultValue:NO]];
20 22
 }
21 23
 
22 24
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
23
-	[super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
24
-	
25
-	UITabBarController* tabBarController = self.bindedViewController;
26
-	
27
-	if (newOptions.bottomTabs.currentTabIndex.hasValue) {
28
-		[tabBarController rnn_setCurrentTabIndex:newOptions.bottomTabs.currentTabIndex.get];
29
-		[newOptions.bottomTabs.currentTabIndex consume];
30
-	}
31
-	
32
-	if (newOptions.bottomTabs.currentTabId.hasValue) {
33
-		[tabBarController rnn_setCurrentTabID:newOptions.bottomTabs.currentTabId.get];
34
-		[newOptions.bottomTabs.currentTabId consume];
35
-	}
36
-	
37
-	if (newOptions.bottomTabs.testID.hasValue) {
38
-		[tabBarController rnn_setTabBarTestID:newOptions.bottomTabs.testID.get];
39
-	}
40
-	
41
-	if (newOptions.bottomTabs.backgroundColor.hasValue) {
42
-		[tabBarController rnn_setTabBarBackgroundColor:newOptions.bottomTabs.backgroundColor.get];
43
-	}
44
-	
45
-	if (newOptions.bottomTabs.barStyle.hasValue) {
46
-		[tabBarController rnn_setTabBarStyle:[RCTConvert UIBarStyle:newOptions.bottomTabs.barStyle.get]];
47
-	}
48
-	
49
-	if (newOptions.bottomTabs.translucent.hasValue) {
50
-		[tabBarController rnn_setTabBarTranslucent:newOptions.bottomTabs.translucent.get];
51
-	}
52
-	
53
-	if (newOptions.bottomTabs.hideShadow.hasValue) {
54
-		[tabBarController rnn_setTabBarHideShadow:newOptions.bottomTabs.hideShadow.get];
55
-	}
56
-	
57
-	if (newOptions.bottomTabs.visible.hasValue) {
58
-		if (newOptions.bottomTabs.animate.hasValue) {
59
-			[tabBarController rnn_setTabBarVisible:newOptions.bottomTabs.visible.get animated:[newOptions.bottomTabs.animate getWithDefaultValue:NO]];
60
-		} else {
61
-			[tabBarController rnn_setTabBarVisible:newOptions.bottomTabs.visible.get animated:NO];
62
-		}
63
-	}
25
+    [super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
26
+
27
+    UITabBarController *tabBarController = self.boundViewController;
28
+
29
+    if (newOptions.bottomTabs.currentTabIndex.hasValue) {
30
+        [tabBarController rnn_setCurrentTabIndex:newOptions.bottomTabs.currentTabIndex.get];
31
+        [newOptions.bottomTabs.currentTabIndex consume];
32
+    }
33
+
34
+    if (newOptions.bottomTabs.currentTabId.hasValue) {
35
+        [tabBarController rnn_setCurrentTabID:newOptions.bottomTabs.currentTabId.get];
36
+        [newOptions.bottomTabs.currentTabId consume];
37
+    }
38
+
39
+    if (newOptions.bottomTabs.testID.hasValue) {
40
+        [tabBarController rnn_setTabBarTestID:newOptions.bottomTabs.testID.get];
41
+    }
42
+
43
+    if (newOptions.bottomTabs.backgroundColor.hasValue) {
44
+        [tabBarController rnn_setTabBarBackgroundColor:newOptions.bottomTabs.backgroundColor.get];
45
+    }
46
+
47
+    if (newOptions.bottomTabs.barStyle.hasValue) {
48
+        [tabBarController rnn_setTabBarStyle:[RCTConvert UIBarStyle:newOptions.bottomTabs.barStyle.get]];
49
+    }
50
+
51
+    if (newOptions.bottomTabs.translucent.hasValue) {
52
+        [tabBarController rnn_setTabBarTranslucent:newOptions.bottomTabs.translucent.get];
53
+    }
54
+
55
+    if (newOptions.bottomTabs.hideShadow.hasValue) {
56
+        [tabBarController rnn_setTabBarHideShadow:newOptions.bottomTabs.hideShadow.get];
57
+    }
58
+
59
+    if (newOptions.bottomTabs.visible.hasValue) {
60
+        if (newOptions.bottomTabs.animate.hasValue) {
61
+            [tabBarController rnn_setTabBarVisible:newOptions.bottomTabs.visible.get animated:[newOptions.bottomTabs.animate getWithDefaultValue:NO]];
62
+        } else {
63
+            [tabBarController rnn_setTabBarVisible:newOptions.bottomTabs.visible.get animated:NO];
64
+        }
65
+    }
66
+}
67
+
68
+- (void)viewDidLayoutSubviews {
69
+    dispatch_async(dispatch_get_main_queue(), ^{
70
+        [self applyDotIndicator];
71
+    });
72
+}
73
+
74
+- (void)applyDotIndicator {
75
+    [self.boundViewController forEachChild:^(UIViewController *child) {
76
+        [self applyDotIndicator:child];
77
+    }];
64 78
 }
65 79
 
66 80
 @end

+ 9
- 9
lib/ios/RNNViewControllerPresenter.m View File

@@ -25,19 +25,19 @@
25 25
 
26 26
 - (void)bindViewController:(UIViewController<RNNLayoutProtocol> *)bindedViewController {
27 27
 	[super bindViewController:bindedViewController];
28
-	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController componentRegistry:_componentRegistry];
28
+	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.boundViewController componentRegistry:_componentRegistry];
29 29
 }
30 30
 
31 31
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
32 32
 	[super applyOptionsOnWillMoveToParentViewController:options];
33
-	UIViewController* viewController = self.bindedViewController;
33
+	UIViewController* viewController = self.boundViewController;
34 34
 	[viewController rnn_setBackButtonIcon:[options.topBar.backButton.icon getWithDefaultValue:nil] withColor:[options.topBar.backButton.color getWithDefaultValue:nil] title:[options.topBar.backButton.showTitle getWithDefaultValue:YES] ? [options.topBar.backButton.title getWithDefaultValue:nil] : @""];
35 35
 }
36 36
 
37 37
 - (void)applyOptions:(RNNNavigationOptions *)options {
38 38
 	[super applyOptions:options];
39 39
 	
40
-	UIViewController* viewController = self.bindedViewController;
40
+	UIViewController* viewController = self.boundViewController;
41 41
 	[viewController rnn_setBackgroundImage:[options.backgroundImage getWithDefaultValue:nil]];
42 42
 	[viewController rnn_setNavigationItemTitle:[options.topBar.title.text getWithDefaultValue:nil]];
43 43
 	[viewController rnn_setTopBarPrefersLargeTitle:[options.topBar.largeTitle.visible getWithDefaultValue:NO]];
@@ -65,7 +65,7 @@
65 65
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
66 66
 	[super applyOptionsOnInit:options];
67 67
 	
68
-	UIViewController* viewController = self.bindedViewController;
68
+	UIViewController* viewController = self.boundViewController;
69 69
 	[viewController rnn_setModalPresentationStyle:[RCTConvert UIModalPresentationStyle:[options.modalPresentationStyle getWithDefaultValue:@"fullScreen"]]];
70 70
 	[viewController rnn_setModalTransitionStyle:[RCTConvert UIModalTransitionStyle:[options.modalTransitionStyle getWithDefaultValue:@"coverVertical"]]];
71 71
 	[viewController rnn_setDrawBehindTopBar:[options.topBar.drawBehind getWithDefaultValue:NO]];
@@ -79,7 +79,7 @@
79 79
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions defaultOptions:(RNNNavigationOptions *)defaultOptions {
80 80
 	[super mergeOptions:newOptions currentOptions:currentOptions defaultOptions:defaultOptions];
81 81
 	
82
-	UIViewController* viewController = self.bindedViewController;
82
+	UIViewController* viewController = self.boundViewController;
83 83
 	
84 84
 	if (newOptions.backgroundImage.hasValue) {
85 85
 		[viewController rnn_setBackgroundImage:newOptions.backgroundImage.get];
@@ -167,14 +167,14 @@
167 167
 }
168 168
 
169 169
 - (void)setCustomNavigationTitleView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
170
-	UIViewController<RNNLayoutProtocol>* viewController = self.bindedViewController;
170
+	UIViewController<RNNLayoutProtocol>* viewController = self.boundViewController;
171 171
 	if (![options.topBar.title.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
172 172
 		readyBlock();
173 173
 		readyBlock = nil;
174 174
 	}
175 175
 	
176 176
 	if (options.topBar.title.component.name.hasValue) {
177
-		_customTitleView = (RNNReactView*)[_componentRegistry createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
177
+		_customTitleView = [_componentRegistry createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
178 178
 		_customTitleView.backgroundColor = UIColor.clearColor;
179 179
 		NSString* alignment = [options.topBar.title.component.alignment getWithDefaultValue:@""];
180 180
 		[_customTitleView setAlignment:alignment inFrame:viewController.navigationController.navigationBar.frame];
@@ -191,7 +191,7 @@
191 191
 
192 192
 - (void)setTitleViewWithSubtitle:(RNNNavigationOptions *)options {
193 193
 	if (!_customTitleView && options.topBar.subtitle.text.hasValue) {
194
-		_titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:options.topBar.title subTitleOptions:options.topBar.subtitle viewController:self.bindedViewController];
194
+		_titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:options.topBar.title subTitleOptions:options.topBar.subtitle viewController:self.boundViewController];
195 195
 		[_titleViewHelper setup];
196 196
 	} else if (_titleViewHelper) {
197 197
 		if (options.topBar.title.text.hasValue) {
@@ -206,7 +206,7 @@
206 206
 }
207 207
 
208 208
 - (void)dealloc {
209
-	[_componentRegistry clearComponentsForParentId:self.bindedComponentId];
209
+	[_componentRegistry clearComponentsForParentId:self.boundComponentId];
210 210
 }
211 211
 
212 212
 

+ 112
- 2
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

@@ -34,6 +34,15 @@
34 34
 		26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 26916C971E4B9E7700D13680 /* RNNReactRootViewCreator.m */; };
35 35
 		2DCD9195200014A900EDC75D /* RNNBridgeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DCD9193200014A900EDC75D /* RNNBridgeManager.h */; };
36 36
 		2DCD9196200014A900EDC75D /* RNNBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DCD9194200014A900EDC75D /* RNNBridgeManager.m */; };
37
+		3098730BC3B4DE41104D9CC4 /* RNNDotIndicatorPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 309878B02F15ECDD1A286722 /* RNNDotIndicatorPresenter.h */; };
38
+		309874B40D202C9718F15CBD /* UIView+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 309877F25920CFE113FADEE0 /* UIView+Utils.h */; };
39
+		30987680135A8C78E62D5B8E /* DotIndicatorOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30987122507D8CBF16624F93 /* DotIndicatorOptions.h */; };
40
+		309877B0B5AAA7788F56F3D9 /* UIViewController+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 30987CF6993B89E85C0BCEE4 /* UIViewController+Utils.h */; };
41
+		309877F473AECC05FB3B9362 /* UITabBarController+RNNUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 30987FED6F982D322416CAF2 /* UITabBarController+RNNUtils.h */; };
42
+		30987AB5137F264FA06DA289 /* DotIndicatorOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 309876223177761614786DCC /* DotIndicatorOptions.m */; };
43
+		30987B23F288EB3A78B7F27C /* RNNDotIndicatorPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 30987E66AA7AB38E7370F8C8 /* RNNDotIndicatorPresenter.m */; };
44
+		30987CA5048A48D6CE76B06C /* DotIndicatorParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 309871FBA64AD937CEF3E191 /* DotIndicatorParser.h */; };
45
+		30987D71FB4FEEAC8D8978E8 /* DotIndicatorParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 309874E37C7E9764C7B694E5 /* DotIndicatorParser.m */; };
37 46
 		390AD477200F499D00A8250D /* RNNSwizzles.h in Headers */ = {isa = PBXBuildFile; fileRef = 390AD475200F499D00A8250D /* RNNSwizzles.h */; };
38 47
 		390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
39 48
 		4534E72520CB6724009F8185 /* RNNLargeTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */; };
@@ -271,6 +280,20 @@
271 280
 		E33AC20020B5BA0B0090DB8A /* RNNSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E33AC1FF20B5BA0B0090DB8A /* RNNSplitViewController.m */; };
272 281
 		E33AC20820B5C4F90090DB8A /* RNNSplitViewOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = E33AC20720B5C4F90090DB8A /* RNNSplitViewOptions.m */; };
273 282
 		E3458D3E20BD9CE40023149B /* RNNPreviewOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = E3458D3D20BD9CE40023149B /* RNNPreviewOptions.m */; };
283
+		E57D1C6322D5CA2400BF7FEE /* RNNDotIndicatorPresenterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 30987758777622B7D6CCD695 /* RNNDotIndicatorPresenterTest.m */; };
284
+		E5F6C3A422DB4D0F0093C2CE /* UIColor+RNNUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F6C39C22DB4D0E0093C2CE /* UIColor+RNNUtils.h */; };
285
+		E5F6C3A522DB4D0F0093C2CE /* UIViewController+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F6C39D22DB4D0E0093C2CE /* UIViewController+Utils.h */; };
286
+		E5F6C3A622DB4D0F0093C2CE /* UIViewController+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C39E22DB4D0E0093C2CE /* UIViewController+Utils.m */; };
287
+		E5F6C3A722DB4D0F0093C2CE /* UIViewController+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C39E22DB4D0E0093C2CE /* UIViewController+Utils.m */; };
288
+		E5F6C3A822DB4D0F0093C2CE /* UIView+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C39F22DB4D0E0093C2CE /* UIView+Utils.m */; };
289
+		E5F6C3A922DB4D0F0093C2CE /* UIView+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C39F22DB4D0E0093C2CE /* UIView+Utils.m */; };
290
+		E5F6C3AA22DB4D0F0093C2CE /* UIColor+RNNUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C3A022DB4D0F0093C2CE /* UIColor+RNNUtils.m */; };
291
+		E5F6C3AB22DB4D0F0093C2CE /* UIColor+RNNUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C3A022DB4D0F0093C2CE /* UIColor+RNNUtils.m */; };
292
+		E5F6C3AC22DB4D0F0093C2CE /* UITabBarController+RNNUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F6C3A122DB4D0F0093C2CE /* UITabBarController+RNNUtils.h */; };
293
+		E5F6C3AD22DB4D0F0093C2CE /* UITabBarController+RNNUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C3A222DB4D0F0093C2CE /* UITabBarController+RNNUtils.m */; };
294
+		E5F6C3AE22DB4D0F0093C2CE /* UITabBarController+RNNUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C3A222DB4D0F0093C2CE /* UITabBarController+RNNUtils.m */; };
295
+		E5F6C3AF22DB4D0F0093C2CE /* UIView+Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = E5F6C3A322DB4D0F0093C2CE /* UIView+Utils.h */; };
296
+		E5F6C3BF22DB51E10093C2CE /* RNNTestBase.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F6C3BD22DB51E00093C2CE /* RNNTestBase.m */; };
274 297
 		E8367B801F7A8A4700675C05 /* VICMAImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = E8367B7E1F7A8A4700675C05 /* VICMAImageView.h */; };
275 298
 		E8367B811F7A8A4700675C05 /* VICMAImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = E8367B7F1F7A8A4700675C05 /* VICMAImageView.m */; };
276 299
 		E83BAD681F2734B500A9F3DD /* RNNNavigationOptionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E83BAD671F2734B500A9F3DD /* RNNNavigationOptionsTest.m */; };
@@ -353,6 +376,20 @@
353 376
 		26916C971E4B9E7700D13680 /* RNNReactRootViewCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNReactRootViewCreator.m; sourceTree = "<group>"; };
354 377
 		2DCD9193200014A900EDC75D /* RNNBridgeManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBridgeManager.h; sourceTree = "<group>"; };
355 378
 		2DCD9194200014A900EDC75D /* RNNBridgeManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBridgeManager.m; sourceTree = "<group>"; };
379
+		30987122507D8CBF16624F93 /* DotIndicatorOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DotIndicatorOptions.h; sourceTree = "<group>"; };
380
+		309871FBA64AD937CEF3E191 /* DotIndicatorParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DotIndicatorParser.h; sourceTree = "<group>"; };
381
+		3098727A36771B4902A14FEA /* RNNTestBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNTestBase.m; sourceTree = "<group>"; };
382
+		309874E37C7E9764C7B694E5 /* DotIndicatorParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DotIndicatorParser.m; sourceTree = "<group>"; };
383
+		309876223177761614786DCC /* DotIndicatorOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DotIndicatorOptions.m; sourceTree = "<group>"; };
384
+		30987758777622B7D6CCD695 /* RNNDotIndicatorPresenterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNDotIndicatorPresenterTest.m; sourceTree = "<group>"; };
385
+		309877F25920CFE113FADEE0 /* UIView+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Utils.h"; sourceTree = "<group>"; };
386
+		309878B02F15ECDD1A286722 /* RNNDotIndicatorPresenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNDotIndicatorPresenter.h; sourceTree = "<group>"; };
387
+		30987CF6993B89E85C0BCEE4 /* UIViewController+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Utils.h"; sourceTree = "<group>"; };
388
+		30987D981545DCBBCCAB34F0 /* UIViewController+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Utils.m"; sourceTree = "<group>"; };
389
+		30987E66AA7AB38E7370F8C8 /* RNNDotIndicatorPresenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNDotIndicatorPresenter.m; sourceTree = "<group>"; };
390
+		30987F749DCD552D95979721 /* UITabBarController+RNNUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITabBarController+RNNUtils.m"; sourceTree = "<group>"; };
391
+		30987F787A14D232AB091E7E /* UIView+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Utils.m"; sourceTree = "<group>"; };
392
+		30987FED6F982D322416CAF2 /* UITabBarController+RNNUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITabBarController+RNNUtils.h"; sourceTree = "<group>"; };
356 393
 		390AD475200F499D00A8250D /* RNNSwizzles.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNSwizzles.h; sourceTree = "<group>"; };
357 394
 		390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
358 395
 		4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNLargeTitleOptions.h; sourceTree = "<group>"; };
@@ -600,6 +637,16 @@
600 637
 		E33AC20720B5C4F90090DB8A /* RNNSplitViewOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSplitViewOptions.m; sourceTree = "<group>"; };
601 638
 		E3458D3C20BD9CA10023149B /* RNNPreviewOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNPreviewOptions.h; sourceTree = "<group>"; };
602 639
 		E3458D3D20BD9CE40023149B /* RNNPreviewOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNPreviewOptions.m; sourceTree = "<group>"; };
640
+		E5F6C39C22DB4D0E0093C2CE /* UIColor+RNNUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIColor+RNNUtils.h"; path = "Utils/UIColor+RNNUtils.h"; sourceTree = "<group>"; };
641
+		E5F6C39D22DB4D0E0093C2CE /* UIViewController+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+Utils.h"; path = "Utils/UIViewController+Utils.h"; sourceTree = "<group>"; };
642
+		E5F6C39E22DB4D0E0093C2CE /* UIViewController+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+Utils.m"; path = "Utils/UIViewController+Utils.m"; sourceTree = "<group>"; };
643
+		E5F6C39F22DB4D0E0093C2CE /* UIView+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+Utils.m"; path = "Utils/UIView+Utils.m"; sourceTree = "<group>"; };
644
+		E5F6C3A022DB4D0F0093C2CE /* UIColor+RNNUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIColor+RNNUtils.m"; path = "Utils/UIColor+RNNUtils.m"; sourceTree = "<group>"; };
645
+		E5F6C3A122DB4D0F0093C2CE /* UITabBarController+RNNUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UITabBarController+RNNUtils.h"; path = "Utils/UITabBarController+RNNUtils.h"; sourceTree = "<group>"; };
646
+		E5F6C3A222DB4D0F0093C2CE /* UITabBarController+RNNUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UITabBarController+RNNUtils.m"; path = "Utils/UITabBarController+RNNUtils.m"; sourceTree = "<group>"; };
647
+		E5F6C3A322DB4D0F0093C2CE /* UIView+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+Utils.h"; path = "Utils/UIView+Utils.h"; sourceTree = "<group>"; };
648
+		E5F6C3BD22DB51E00093C2CE /* RNNTestBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNTestBase.m; sourceTree = "<group>"; };
649
+		E5F6C3BE22DB51E00093C2CE /* RNNTestBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNTestBase.h; sourceTree = "<group>"; };
603 650
 		E8367B7E1F7A8A4700675C05 /* VICMAImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VICMAImageView.h; sourceTree = "<group>"; };
604 651
 		E8367B7F1F7A8A4700675C05 /* VICMAImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VICMAImageView.m; sourceTree = "<group>"; };
605 652
 		E83BAD671F2734B500A9F3DD /* RNNNavigationOptionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNNavigationOptionsTest.m; sourceTree = "<group>"; };
@@ -820,6 +867,8 @@
820 867
 				5039558A2174829400B0A663 /* IntNumberParser.m */,
821 868
 				503955992174867000B0A663 /* DoubleParser.h */,
822 869
 				5039559A2174867000B0A663 /* DoubleParser.m */,
870
+				309874E37C7E9764C7B694E5 /* DotIndicatorParser.m */,
871
+				309871FBA64AD937CEF3E191 /* DotIndicatorParser.h */,
823 872
 			);
824 873
 			name = Parsers;
825 874
 			sourceTree = "<group>";
@@ -883,6 +932,8 @@
883 932
 				E3458D3D20BD9CE40023149B /* RNNPreviewOptions.m */,
884 933
 				506317AC220B550600B26FC3 /* RNNInsetsOptions.h */,
885 934
 				506317AD220B550600B26FC3 /* RNNInsetsOptions.m */,
935
+				309876223177761614786DCC /* DotIndicatorOptions.m */,
936
+				30987122507D8CBF16624F93 /* DotIndicatorOptions.h */,
886 937
 			);
887 938
 			name = Options;
888 939
 			sourceTree = "<group>";
@@ -902,6 +953,8 @@
902 953
 				5012240921735959000F5F98 /* RNNSideMenuPresenter.m */,
903 954
 				651E1F8B21FD63F400DFEA19 /* RNNSplitViewControllerPresenter.h */,
904 955
 				651E1F8C21FD642100DFEA19 /* RNNSplitViewControllerPresenter.m */,
956
+				30987E66AA7AB38E7370F8C8 /* RNNDotIndicatorPresenter.m */,
957
+				309878B02F15ECDD1A286722 /* RNNDotIndicatorPresenter.h */,
905 958
 			);
906 959
 			name = Presenters;
907 960
 			sourceTree = "<group>";
@@ -975,6 +1028,7 @@
975 1028
 		7B49FEBC1E95090800DEB3EA /* ReactNativeNavigationTests */ = {
976 1029
 			isa = PBXGroup;
977 1030
 			children = (
1031
+				E5F6C3BC22DB51E00093C2CE /* utils */,
978 1032
 				7B49FEC61E95098500DEB3EA /* RNNControllerFactoryTest.m */,
979 1033
 				7B49FEC71E95098500DEB3EA /* RNNExternalComponentStoreTest.m */,
980 1034
 				7B49FEC81E95098500DEB3EA /* RNNModalManagerTest.m */,
@@ -1002,6 +1056,7 @@
1002 1056
 				509B247F217873FF00C83C23 /* UINavigationController+RNNOptionsTest.m */,
1003 1057
 				50AB0B1E22562FA10039DAED /* UIViewController+LayoutProtocolTest.m */,
1004 1058
 				505963F622676A0000EBB63C /* RNNLayoutManagerTest.m */,
1059
+				30987758777622B7D6CCD695 /* RNNDotIndicatorPresenterTest.m */,
1005 1060
 			);
1006 1061
 			path = ReactNativeNavigationTests;
1007 1062
 			sourceTree = "<group>";
@@ -1043,6 +1098,7 @@
1043 1098
 		D8AFADB41BEE6F3F00A4592D = {
1044 1099
 			isa = PBXGroup;
1045 1100
 			children = (
1101
+				E5F6C39B22DB4CB90093C2CE /* Utils */,
1046 1102
 				214545271F4DC7ED006E8DA1 /* Helpers */,
1047 1103
 				7BA500731E2544B9001B9E1B /* ReactNativeNavigation.h */,
1048 1104
 				7BA500741E2544B9001B9E1B /* ReactNativeNavigation.m */,
@@ -1084,10 +1140,41 @@
1084 1140
 			name = Products;
1085 1141
 			sourceTree = "<group>";
1086 1142
 		};
1143
+		E5F6C39B22DB4CB90093C2CE /* Utils */ = {
1144
+			isa = PBXGroup;
1145
+			children = (
1146
+				E5F6C39C22DB4D0E0093C2CE /* UIColor+RNNUtils.h */,
1147
+				E5F6C3A022DB4D0F0093C2CE /* UIColor+RNNUtils.m */,
1148
+				E5F6C3A122DB4D0F0093C2CE /* UITabBarController+RNNUtils.h */,
1149
+				E5F6C3A222DB4D0F0093C2CE /* UITabBarController+RNNUtils.m */,
1150
+				E5F6C3A322DB4D0F0093C2CE /* UIView+Utils.h */,
1151
+				E5F6C39F22DB4D0E0093C2CE /* UIView+Utils.m */,
1152
+				E5F6C39D22DB4D0E0093C2CE /* UIViewController+Utils.h */,
1153
+				E5F6C39E22DB4D0E0093C2CE /* UIViewController+Utils.m */,
1154
+			);
1155
+			name = Utils;
1156
+			sourceTree = "<group>";
1157
+		};
1158
+		E5F6C3BC22DB51E00093C2CE /* utils */ = {
1159
+			isa = PBXGroup;
1160
+			children = (
1161
+				E5F6C3BD22DB51E00093C2CE /* RNNTestBase.m */,
1162
+				E5F6C3BE22DB51E00093C2CE /* RNNTestBase.h */,
1163
+			);
1164
+			path = utils;
1165
+			sourceTree = "<group>";
1166
+		};
1087 1167
 		E8367B791F77BA1F00675C05 /* Recovered References */ = {
1088 1168
 			isa = PBXGroup;
1089 1169
 			children = (
1090 1170
 				E8A5CD581F48CCC300E89D0D /* RNNNavigationController.h */,
1171
+				30987F787A14D232AB091E7E /* UIView+Utils.m */,
1172
+				30987D981545DCBBCCAB34F0 /* UIViewController+Utils.m */,
1173
+				30987F749DCD552D95979721 /* UITabBarController+RNNUtils.m */,
1174
+				309877F25920CFE113FADEE0 /* UIView+Utils.h */,
1175
+				30987CF6993B89E85C0BCEE4 /* UIViewController+Utils.h */,
1176
+				30987FED6F982D322416CAF2 /* UITabBarController+RNNUtils.h */,
1177
+				3098727A36771B4902A14FEA /* RNNTestBase.m */,
1091 1178
 			);
1092 1179
 			name = "Recovered References";
1093 1180
 			sourceTree = "<group>";
@@ -1168,6 +1255,7 @@
1168 1255
 				50E02BDD21A6EE7900A43942 /* SideMenuOpenGestureModeParser.h in Headers */,
1169 1256
 				5038A3B5216DF602009280BC /* UINavigationController+RNNOptions.h in Headers */,
1170 1257
 				50E5F78D223F9FAF002AFEAD /* RNNElementTransitionOptions.h in Headers */,
1258
+				E5F6C3A422DB4D0F0093C2CE /* UIColor+RNNUtils.h in Headers */,
1171 1259
 				5038A3C1216E1E66009280BC /* RNNFontAttributesCreator.h in Headers */,
1172 1260
 				5016E8EF20209690009D4F7C /* RNNCustomTitleView.h in Headers */,
1173 1261
 				50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */,
@@ -1189,6 +1277,7 @@
1189 1277
 				5012242621737278000F5F98 /* NullImage.h in Headers */,
1190 1278
 				5038A3B9216DFCFD009280BC /* UITabBarController+RNNOptions.h in Headers */,
1191 1279
 				50644A2020E11A720026709C /* Constants.h in Headers */,
1280
+				E5F6C3AF22DB4D0F0093C2CE /* UIView+Utils.h in Headers */,
1192 1281
 				5012241E217366D4000F5F98 /* ColorParser.h in Headers */,
1193 1282
 				E8367B801F7A8A4700675C05 /* VICMAImageView.h in Headers */,
1194 1283
 				263905E61E4CAC950023D7D3 /* RNNSideMenuChildVC.h in Headers */,
@@ -1203,10 +1292,12 @@
1203 1292
 				50E5F7952240EBD6002AFEAD /* RNNAnimationsTransitionDelegate.h in Headers */,
1204 1293
 				7B1126A71E2D2B6C00F9B03B /* RNNEventEmitter.h in Headers */,
1205 1294
 				E8A430111F9CB87B00B61A20 /* RNNAnimatedView.h in Headers */,
1295
+				E5F6C3AC22DB4D0F0093C2CE /* UITabBarController+RNNUtils.h in Headers */,
1206 1296
 				506317AA220B547400B26FC3 /* UIImage+insets.h in Headers */,
1207 1297
 				5038A3C6216E2D93009280BC /* Number.h in Headers */,
1208 1298
 				50887C1520ECC5C200D06111 /* RNNButtonOptions.h in Headers */,
1209 1299
 				5049593E216F5D73006D2B81 /* BoolParser.h in Headers */,
1300
+				E5F6C3A522DB4D0F0093C2CE /* UIViewController+Utils.h in Headers */,
1210 1301
 				50C4A496206BDDBB00DB292E /* RNNSubtitleOptions.h in Headers */,
1211 1302
 				5039559B2174867000B0A663 /* DoubleParser.h in Headers */,
1212 1303
 				E8AEDB3C1F55A1C2000F5A6A /* RNNElementView.h in Headers */,
@@ -1257,6 +1348,12 @@
1257 1348
 				5049595A216F6B46006D2B81 /* NullDictionary.h in Headers */,
1258 1349
 				501224062173592D000F5F98 /* RNNTabBarPresenter.h in Headers */,
1259 1350
 				50706E6D20CE7CA5003345C3 /* UIImage+tint.h in Headers */,
1351
+				309874B40D202C9718F15CBD /* UIView+Utils.h in Headers */,
1352
+				309877B0B5AAA7788F56F3D9 /* UIViewController+Utils.h in Headers */,
1353
+				30987680135A8C78E62D5B8E /* DotIndicatorOptions.h in Headers */,
1354
+				30987CA5048A48D6CE76B06C /* DotIndicatorParser.h in Headers */,
1355
+				3098730BC3B4DE41104D9CC4 /* RNNDotIndicatorPresenter.h in Headers */,
1356
+				309877F473AECC05FB3B9362 /* UITabBarController+RNNUtils.h in Headers */,
1260 1357
 			);
1261 1358
 			runOnlyForDeploymentPostprocessing = 0;
1262 1359
 		};
@@ -1353,6 +1450,7 @@
1353 1450
 			buildActionMask = 2147483647;
1354 1451
 			files = (
1355 1452
 				502F0E142178CF8200367CC3 /* UIViewController+RNNOptionsTest.m in Sources */,
1453
+				E57D1C6322D5CA2400BF7FEE /* RNNDotIndicatorPresenterTest.m in Sources */,
1356 1454
 				5038A377216CF252009280BC /* UITabBarController+RNNOptionsTest.m in Sources */,
1357 1455
 				E83BAD7C1F27643000A9F3DD /* RNNTestRootViewCreator.m in Sources */,
1358 1456
 				502F0E162178D09600367CC3 /* RNNBasePresenterTest.m in Sources */,
@@ -1361,12 +1459,14 @@
1361 1459
 				506F630F216A5AD700AD0D0A /* RNNViewControllerPresenterTest.m in Sources */,
1362 1460
 				505963F722676A0000EBB63C /* RNNLayoutManagerTest.m in Sources */,
1363 1461
 				7B49FECE1E95098500DEB3EA /* RNNCommandsHandlerTest.m in Sources */,
1462
+				E5F6C3AB22DB4D0F0093C2CE /* UIColor+RNNUtils.m in Sources */,
1364 1463
 				5038A379216D01F6009280BC /* RNNBottomTabOptionsTest.m in Sources */,
1365 1464
 				E83BAD681F2734B500A9F3DD /* RNNNavigationOptionsTest.m in Sources */,
1366 1465
 				505EDD32214E4BE80071C7DE /* RNNNavigationControllerTest.m in Sources */,
1367 1466
 				50AB0B1F22562FA10039DAED /* UIViewController+LayoutProtocolTest.m in Sources */,
1368 1467
 				7B49FECF1E95098500DEB3EA /* RNNNavigationStackManagerTest.m in Sources */,
1369 1468
 				509B2480217873FF00C83C23 /* UINavigationController+RNNOptionsTest.m in Sources */,
1469
+				E5F6C3BF22DB51E10093C2CE /* RNNTestBase.m in Sources */,
1370 1470
 				506F630D216A599300AD0D0A /* RNNTabBarControllerTest.m in Sources */,
1371 1471
 				7B49FECB1E95098500DEB3EA /* RNNControllerFactoryTest.m in Sources */,
1372 1472
 				50206A6D21AFE75400B7BB1A /* RNNSideMenuParserTest.m in Sources */,
@@ -1375,6 +1475,9 @@
1375 1475
 				5085DD2D21DCF75A0032E64B /* RNNSideMenuControllerTest.m in Sources */,
1376 1476
 				E83BAD791F27416B00A9F3DD /* RNNRootViewControllerTest.m in Sources */,
1377 1477
 				50CE8503217C6C9B00084EBF /* RNNSideMenuPresenterTest.m in Sources */,
1478
+				E5F6C3AE22DB4D0F0093C2CE /* UITabBarController+RNNUtils.m in Sources */,
1479
+				E5F6C3A922DB4D0F0093C2CE /* UIView+Utils.m in Sources */,
1480
+				E5F6C3A722DB4D0F0093C2CE /* UIViewController+Utils.m in Sources */,
1378 1481
 				E8DA243D1F973C1900CD552B /* RNNTransitionStateHolderTest.m in Sources */,
1379 1482
 				7B49FECC1E95098500DEB3EA /* RNNExternalComponentStoreTest.m in Sources */,
1380 1483
 			);
@@ -1394,6 +1497,7 @@
1394 1497
 				5012241B21736678000F5F98 /* Image.m in Sources */,
1395 1498
 				50495943216F5E5D006D2B81 /* NullBool.m in Sources */,
1396 1499
 				5038A3C7216E2D93009280BC /* Number.m in Sources */,
1500
+				E5F6C3A622DB4D0F0093C2CE /* UIViewController+Utils.m in Sources */,
1397 1501
 				50451D0E2042F70900695F00 /* RNNTransition.m in Sources */,
1398 1502
 				5048862E20BE976D000908DE /* RNNLayoutOptions.m in Sources */,
1399 1503
 				501CD320214A5B6900A6E225 /* RNNLayoutInfo.m in Sources */,
@@ -1438,6 +1542,7 @@
1438 1542
 				50395590217482FE00B0A663 /* NullIntNumber.m in Sources */,
1439 1543
 				E8A5CD631F49114F00E89D0D /* RNNElement.m in Sources */,
1440 1544
 				5038A3CB216E328A009280BC /* Param.m in Sources */,
1545
+				E5F6C3AA22DB4D0F0093C2CE /* UIColor+RNNUtils.m in Sources */,
1441 1546
 				651E1F8A21FD624600DFEA19 /* UISplitViewController+RNNOptions.m in Sources */,
1442 1547
 				50395594217485B000B0A663 /* Double.m in Sources */,
1443 1548
 				504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */,
@@ -1462,6 +1567,7 @@
1462 1567
 				507F43CA1FF4F9CC00D9425B /* RNNTopTabOptions.m in Sources */,
1463 1568
 				26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */,
1464 1569
 				5064495E20DC62B90026709C /* RNNSideMenuSideOptions.m in Sources */,
1570
+				E5F6C3AD22DB4D0F0093C2CE /* UITabBarController+RNNUtils.m in Sources */,
1465 1571
 				214545251F4DC125006E8DA1 /* RNNUIBarButtonItem.m in Sources */,
1466 1572
 				263905B81E4C6F440023D7D3 /* UIViewController+MMDrawerController.m in Sources */,
1467 1573
 				505EDD3D214FA8000071C7DE /* RNNViewControllerPresenter.m in Sources */,
@@ -1479,6 +1585,7 @@
1479 1585
 				501224072173592D000F5F98 /* RNNTabBarPresenter.m in Sources */,
1480 1586
 				50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */,
1481 1587
 				5039559C2174867000B0A663 /* DoubleParser.m in Sources */,
1588
+				E5F6C3A822DB4D0F0093C2CE /* UIView+Utils.m in Sources */,
1482 1589
 				5049593F216F5D73006D2B81 /* BoolParser.m in Sources */,
1483 1590
 				50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,
1484 1591
 				50E5F792223FA04C002AFEAD /* RNNAnimationConfigurationOptions.m in Sources */,
@@ -1510,6 +1617,9 @@
1510 1617
 				E8E518371F83B94A000467AC /* RNNViewLocation.m in Sources */,
1511 1618
 				E3458D3E20BD9CE40023149B /* RNNPreviewOptions.m in Sources */,
1512 1619
 				5012242B217372B3000F5F98 /* ImageParser.m in Sources */,
1620
+				30987AB5137F264FA06DA289 /* DotIndicatorOptions.m in Sources */,
1621
+				30987D71FB4FEEAC8D8978E8 /* DotIndicatorParser.m in Sources */,
1622
+				30987B23F288EB3A78B7F27C /* RNNDotIndicatorPresenter.m in Sources */,
1513 1623
 			);
1514 1624
 			runOnlyForDeploymentPostprocessing = 0;
1515 1625
 		};
@@ -1679,7 +1789,7 @@
1679 1789
 			buildSettings = {
1680 1790
 				CLANG_ENABLE_MODULES = YES;
1681 1791
 				CLANG_WARN_STRICT_PROTOTYPES = NO;
1682
-				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
1792
+				GCC_TREAT_WARNINGS_AS_ERRORS = NO;
1683 1793
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1684 1794
 				OTHER_LDFLAGS = "-ObjC";
1685 1795
 				PRODUCT_NAME = ReactNativeNavigation;
@@ -1696,7 +1806,7 @@
1696 1806
 			buildSettings = {
1697 1807
 				CLANG_ENABLE_MODULES = YES;
1698 1808
 				CLANG_WARN_STRICT_PROTOTYPES = NO;
1699
-				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
1809
+				GCC_TREAT_WARNINGS_AS_ERRORS = NO;
1700 1810
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1701 1811
 				OTHER_LDFLAGS = "-ObjC";
1702 1812
 				PRODUCT_NAME = ReactNativeNavigation;

+ 26
- 26
lib/ios/ReactNativeNavigationTests/RNNBasePresenterTest.m View File

@@ -6,10 +6,10 @@
6 6
 
7 7
 @interface RNNBottomTabPresenterTest : XCTestCase
8 8
 
9
-@property (nonatomic, strong) RNNBasePresenter *uut;
10
-@property (nonatomic, strong) RNNNavigationOptions *options;
11
-@property (nonatomic, strong) RNNRootViewController* bindedViewController;
12
-@property (nonatomic, strong) id mockBindedViewController;
9
+@property(nonatomic, strong) RNNBasePresenter *uut;
10
+@property(nonatomic, strong) RNNNavigationOptions *options;
11
+@property(nonatomic, strong) RNNRootViewController *bindedViewController;
12
+@property(nonatomic, strong) id mockBindedViewController;
13 13
 
14 14
 @end
15 15
 
@@ -18,46 +18,46 @@
18 18
 - (void)setUp {
19 19
     [super setUp];
20 20
     self.uut = [[RNNBasePresenter alloc] init];
21
-	  self.bindedViewController = [RNNRootViewController new];
21
+    self.bindedViewController = [RNNRootViewController new];
22 22
     self.mockBindedViewController = [OCMockObject partialMockForObject:self.bindedViewController];
23 23
     [self.uut bindViewController:self.mockBindedViewController];
24 24
     self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
25 25
 }
26 26
 
27 27
 - (void)tearDown {
28
-	[super tearDown];
29
-	[self.mockBindedViewController stopMocking];
30
-	self.bindedViewController = nil;
28
+    [super tearDown];
29
+    [self.mockBindedViewController stopMocking];
30
+    self.bindedViewController = nil;
31 31
 }
32 32
 
33 33
 - (void)testApplyOptions_shouldSetTabBarItemBadgeOnlyWhenParentIsUITabBarController {
34
-	[[self.mockBindedViewController reject] rnn_setTabBarItemBadge:[OCMArg any]];
35
-	[self.uut applyOptions:self.options];
36
-	[self.mockBindedViewController verify];
34
+    [[self.mockBindedViewController reject] rnn_setTabBarItemBadge:[OCMArg any]];
35
+    [self.uut applyOptions:self.options];
36
+    [self.mockBindedViewController verify];
37 37
 }
38 38
 
39 39
 - (void)testApplyOptions_shouldSetTabBarItemBadgeWithValue {
40
-	OCMStub([self.mockBindedViewController parentViewController]).andReturn([UITabBarController new]);
41
-	self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
42
-	[[self.mockBindedViewController expect] rnn_setTabBarItemBadge:@"badge"];
43
-	[self.uut applyOptions:self.options];
44
-	[self.mockBindedViewController verify];
40
+    OCMStub([self.mockBindedViewController parentViewController]).andReturn([UITabBarController new]);
41
+    self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
42
+    [[self.mockBindedViewController expect] rnn_setTabBarItemBadge:self.options.bottomTab];
43
+    [self.uut applyOptions:self.options];
44
+    [self.mockBindedViewController verify];
45 45
 }
46 46
 
47 47
 - (void)testApplyOptions_setTabBarItemBadgeShouldNotCalledOnUITabBarController {
48
-	[self.uut bindViewController:self.mockBindedViewController];
49
-	self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
50
-	[[self.mockBindedViewController reject] rnn_setTabBarItemBadge:@"badge"];
51
-	[self.uut applyOptions:self.options];
52
-	[self.mockBindedViewController verify];
48
+    [self.uut bindViewController:self.mockBindedViewController];
49
+    self.options.bottomTab.badge = [[Text alloc] initWithValue:@"badge"];
50
+    [[self.mockBindedViewController reject] rnn_setTabBarItemBadge:[[RNNBottomTabOptions alloc] initWithDict:@{@"badge": @"badge"}]];
51
+    [self.uut applyOptions:self.options];
52
+    [self.mockBindedViewController verify];
53 53
 }
54 54
 
55 55
 - (void)testApplyOptions_setTabBarItemBadgeShouldWhenNoValue {
56
-	[self.uut bindViewController:self.mockBindedViewController];
57
-	self.options.bottomTab.badge = nil;
58
-	[[self.mockBindedViewController reject] rnn_setTabBarItemBadge:[OCMArg any]];
59
-	[self.uut applyOptions:self.options];
60
-	[self.mockBindedViewController verify];
56
+    [self.uut bindViewController:self.mockBindedViewController];
57
+    self.options.bottomTab.badge = nil;
58
+    [[self.mockBindedViewController reject] rnn_setTabBarItemBadge:[OCMArg any]];
59
+    [self.uut applyOptions:self.options];
60
+    [self.mockBindedViewController verify];
61 61
 }
62 62
 
63 63
 @end

+ 158
- 0
lib/ios/ReactNativeNavigationTests/RNNDotIndicatorPresenterTest.m View File

@@ -0,0 +1,158 @@
1
+#import <XCTest/XCTest.h>
2
+#import <OCMock/OCMock.h>
3
+#import "RNNDotIndicatorPresenter.h"
4
+#import "DotIndicatorOptions.h"
5
+#import "RNNTabBarController.h"
6
+#import "RNNRootViewController.h"
7
+#import "RNNTestBase.h"
8
+#import "UITabBarController+RNNUtils.h"
9
+
10
+@interface RNNDotIndicatorPresenterTest : RNNTestBase
11
+@property(nonatomic, strong) id uut;
12
+@property(nonatomic, strong) RNNRootViewController *child;
13
+@property(nonatomic, strong) id bottomTabs;
14
+@end
15
+
16
+@implementation RNNDotIndicatorPresenterTest
17
+- (void)setUp {
18
+    [super setUp];
19
+    self.uut = [OCMockObject partialMockForObject:[RNNDotIndicatorPresenter new]];
20
+    self.bottomTabs = [OCMockObject partialMockForObject:[RNNTabBarController new]];
21
+    self.child = [self createChild];
22
+    [self.bottomTabs addChildViewController:self.child];
23
+
24
+    [self setupTopLevelUI:self.bottomTabs];
25
+}
26
+
27
+- (void)tearDown {
28
+    [self tearDownTopLevelUI:_bottomTabs];
29
+    [super tearDown];
30
+}
31
+
32
+- (void)testApply_doesNothingIfDoesNotHaveValue {
33
+    DotIndicatorOptions *empty = [DotIndicatorOptions new];
34
+    [[self uut] apply:self.child :empty];
35
+    XCTAssertFalse([self tabHasIndicator]);
36
+}
37
+
38
+- (void)testApply_indicatorIsAddedToTabView {
39
+    [self applyIndicator];
40
+    XCTAssertTrue([self tabHasIndicator]);
41
+}
42
+
43
+- (void)testApply_indicatorIsRemovedIfNotVisible {
44
+    [self applyIndicator];
45
+    XCTAssertTrue([self tabHasIndicator]);
46
+
47
+    DotIndicatorOptions *options = [DotIndicatorOptions new];
48
+    options.visible = [[Bool alloc] initWithBOOL:NO];
49
+    [[self uut] apply:self.child :options];
50
+
51
+    XCTAssertFalse([self tabHasIndicator]);
52
+}
53
+
54
+- (void)testApply_invisibleIndicatorIsNotAdded {
55
+    DotIndicatorOptions *options = [DotIndicatorOptions new];
56
+    options.visible = [[Bool alloc] initWithBOOL:NO];
57
+    [[self uut] apply:self.child :options];
58
+
59
+    XCTAssertFalse([self tabHasIndicator]);
60
+}
61
+
62
+- (void)testApply_itDoesNotRecreateIfEqualToCurrentlyVisibleIndicator {
63
+    [self applyIndicator];
64
+    UIView *indicator1 = [self getIndicator];
65
+
66
+    [self applyIndicator];
67
+    UIView *indicator2 = [self getIndicator];
68
+    XCTAssertEqualObjects(indicator1, indicator2);
69
+}
70
+
71
+- (void)testApply_itAddsIndicatorToCorrectTabView {
72
+    [self applyIndicator];
73
+    UIView *indicator1 = [self getIndicator];
74
+    XCTAssertEqualObjects([indicator1 superview], [_bottomTabs getTabView:0]);
75
+}
76
+
77
+- (void)testApply_itRemovesPreviousDotIndicator {
78
+    NSUInteger childCountBeforeApplyingIndicator = [[_bottomTabs getTabView:0] subviews].count;
79
+    [self applyIndicator];
80
+    NSUInteger childCountAfterApplyingIndicatorOnce = [[_bottomTabs getTabView:0] subviews].count;
81
+    XCTAssertEqual(childCountBeforeApplyingIndicator + 1, childCountAfterApplyingIndicatorOnce);
82
+
83
+    [self applyIndicator:[UIColor greenColor]];
84
+    NSUInteger childCountAfterApplyingIndicatorTwice = [[_bottomTabs getTabView:0] subviews].count;
85
+    XCTAssertEqual([[self getIndicator] backgroundColor], [UIColor greenColor]);
86
+    XCTAssertEqual(childCountAfterApplyingIndicatorOnce, childCountAfterApplyingIndicatorTwice);
87
+}
88
+
89
+- (void)testApply_itRemovesPreviousIndicator {
90
+    DotIndicatorOptions *options = [DotIndicatorOptions new];
91
+    options.visible = [[Bool alloc] initWithBOOL:YES];
92
+    options.color = [[Color alloc] initWithValue:[UIColor redColor]];
93
+    options.size = [[Number alloc] initWithValue:[[NSNumber alloc] initWithInt:8]];
94
+
95
+    [[self uut] apply:self.child :options];
96
+    XCTAssertTrue([self tabHasIndicator]);
97
+
98
+    options.visible = [[Bool alloc] initWithBOOL:NO];
99
+    [[self uut] apply:self.child :options];
100
+    XCTAssertFalse([self tabHasIndicator]);
101
+}
102
+
103
+- (void)testApply_indicatorIsAlignedToTopRightOfIcon {
104
+    DotIndicatorOptions *options = [DotIndicatorOptions new];
105
+    options.visible = [[Bool alloc] initWithBOOL:YES];
106
+    options.size = [[Number alloc] initWithValue:[[NSNumber alloc] initWithInt:8]];
107
+    [[self uut] apply:self.child :options];
108
+    UIView *indicator = [self getIndicator];
109
+    UIView * icon = [_bottomTabs getTabIcon:0];
110
+
111
+    NSArray<NSLayoutConstraint *> *alignmentConstraints = [_bottomTabs getTabView:0].constraints;
112
+    XCTAssertEqual([alignmentConstraints count], 2);
113
+    XCTAssertEqual([alignmentConstraints[0] constant], -4);
114
+    XCTAssertEqual([alignmentConstraints[0] firstItem], indicator);
115
+    XCTAssertEqual([alignmentConstraints[0] secondItem], icon);
116
+    XCTAssertEqual([alignmentConstraints[0] firstAttribute], NSLayoutAttributeLeft);
117
+    XCTAssertEqual([alignmentConstraints[0] secondAttribute], NSLayoutAttributeRight);
118
+
119
+    XCTAssertEqual([alignmentConstraints[1] constant], -4);
120
+    XCTAssertEqual([alignmentConstraints[1] firstItem], indicator);
121
+    XCTAssertEqual([alignmentConstraints[1] secondItem], icon);
122
+    XCTAssertEqual([alignmentConstraints[1] firstAttribute], NSLayoutAttributeTop);
123
+    XCTAssertEqual([alignmentConstraints[1] secondAttribute], NSLayoutAttributeTop);
124
+
125
+    NSArray *sizeConstraints = indicator.constraints;
126
+    XCTAssertEqual([sizeConstraints count], 2);
127
+    XCTAssertEqual([sizeConstraints[0] constant], 8);
128
+    XCTAssertEqual([sizeConstraints[1] constant], 8);
129
+}
130
+
131
+- (void)applyIndicator {
132
+    [self applyIndicator:[UIColor redColor]];
133
+}
134
+
135
+- (void)applyIndicator:(UIColor *) color {
136
+    DotIndicatorOptions *options = [DotIndicatorOptions new];
137
+    options.visible = [[Bool alloc] initWithBOOL:YES];
138
+    options.color = [[Color alloc] initWithValue:color];
139
+    [[self uut] apply:self.child :options];
140
+}
141
+
142
+- (RNNRootViewController *)createChild {
143
+    RNNNavigationOptions *options = [RNNNavigationOptions new];
144
+    options.bottomTab = [RNNBottomTabOptions new];
145
+    id img = [OCMockObject partialMockForObject:[UIImage new]];
146
+
147
+    options.bottomTab.icon = [[Image alloc] initWithValue:img];
148
+    return [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:options defaultOptions:nil];
149
+}
150
+
151
+- (BOOL)tabHasIndicator {
152
+    return [self.child tabBarItem].tag > 0;
153
+}
154
+
155
+- (UIView *)getIndicator {
156
+    return [self tabHasIndicator] ? [[((UITabBarController *) _bottomTabs) tabBar] viewWithTag:_child.tabBarItem.tag] : nil;
157
+}
158
+@end

+ 2
- 2
lib/ios/ReactNativeNavigationTests/RNNNavigationControllerPresenterTest.m View File

@@ -47,14 +47,14 @@
47 47
 	_options.topBar.largeTitle.visible = [[Bool alloc] initWithBOOL:YES];
48 48
 	
49 49
 	[self.uut applyOptionsBeforePopping:self.options];
50
-	XCTAssertTrue([[self.uut.bindedViewController navigationBar] prefersLargeTitles]);
50
+	XCTAssertTrue([[self.uut.boundViewController navigationBar] prefersLargeTitles]);
51 51
 }
52 52
 
53 53
 - (void)testApplyOptionsBeforePoppingShouldSetDefaultLargeTitleFalseForPoppingViewController {
54 54
 	_options.topBar.largeTitle.visible = nil;
55 55
 	
56 56
 	[self.uut applyOptionsBeforePopping:self.options];
57
-	XCTAssertFalse([[self.uut.bindedViewController navigationBar] prefersLargeTitles]);
57
+	XCTAssertFalse([[self.uut.boundViewController navigationBar] prefersLargeTitles]);
58 58
 }
59 59
 
60 60
 @end

+ 99
- 95
lib/ios/ReactNativeNavigationTests/RNNTabBarControllerTest.m View File

@@ -1,166 +1,170 @@
1 1
 #import <XCTest/XCTest.h>
2 2
 #import "RNNTabBarController.h"
3
-#import "RNNNavigationOptions.h"
4
-#import "RNNTabBarPresenter.h"
5 3
 #import "RNNRootViewController.h"
6 4
 #import "RNNNavigationController.h"
7 5
 #import <OCMock/OCMock.h>
8 6
 
9 7
 @interface RNNTabBarControllerTest : XCTestCase
10 8
 
11
-@property (nonatomic, strong) id mockUut;
12
-@property (nonatomic, strong) id mockChildViewController;
13
-@property (nonatomic, strong) id mockEventEmmiter;
14
-@property (nonatomic, strong) id mockTabBarPresenter;
9
+@property(nonatomic, strong) RNNTabBarController * uut;
10
+@property(nonatomic, strong) id mockChildViewController;
11
+@property(nonatomic, strong) id mockEventEmitter;
12
+@property(nonatomic, strong) id mockTabBarPresenter;
15 13
 
16 14
 @end
17 15
 
18 16
 @implementation RNNTabBarControllerTest
19 17
 
20 18
 - (void)setUp {
21
-	[super setUp];
22
-	
23
-	id tabBarClassMock = OCMClassMock([RNNTabBarController class]);
24
-	OCMStub([tabBarClassMock parentViewController]).andReturn([OCMockObject partialMockForObject:[RNNTabBarController new]]);
19
+    [super setUp];
25 20
 
26
-	self.mockTabBarPresenter = [OCMockObject partialMockForObject:[[RNNTabBarPresenter alloc] init]];
27
-	self.mockChildViewController = [OCMockObject partialMockForObject:[RNNRootViewController new]];
28
-	self.mockEventEmmiter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
29
-	self.mockUut = [OCMockObject partialMockForObject:[[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:self.mockTabBarPresenter eventEmitter:self.mockEventEmmiter childViewControllers:@[[[UIViewController alloc] init]]]];
30
-	OCMStub([self.mockUut selectedViewController]).andReturn(self.mockChildViewController);
21
+    id tabBarClassMock = OCMClassMock([RNNTabBarController class]);
22
+    OCMStub([tabBarClassMock parentViewController]).andReturn([OCMockObject partialMockForObject:[RNNTabBarController new]]);
23
+
24
+    self.mockTabBarPresenter = [OCMockObject partialMockForObject:[[RNNTabBarPresenter alloc] init]];
25
+    self.mockChildViewController = [OCMockObject partialMockForObject:[RNNRootViewController new]];
26
+    self.mockEventEmitter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
27
+    self.uut = [OCMockObject partialMockForObject:[[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:self.mockTabBarPresenter eventEmitter:self.mockEventEmitter childViewControllers:@[[[UIViewController alloc] init]]]];
28
+    OCMStub([self.uut selectedViewController]).andReturn(self.mockChildViewController);
31 29
 }
32 30
 
33 31
 - (void)testInitWithLayoutInfo_shouldBindPresenter {
34
-	XCTAssertNotNil([self.mockUut presenter]);
32
+    XCTAssertNotNil([self.uut presenter]);
35 33
 }
36 34
 
37 35
 - (void)testInitWithLayoutInfo_shouldSetMultipleViewControllers {
38
-	UIViewController* vc1 = [[UIViewController alloc] init];
39
-	UIViewController* vc2 = [[UIViewController alloc] init];
40
-	
41
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init] eventEmitter:nil childViewControllers:@[vc1, vc2]];
42
-	XCTAssertTrue(uut.viewControllers.count == 2);
36
+    UIViewController *vc1 = [[UIViewController alloc] init];
37
+    UIViewController *vc2 = [[UIViewController alloc] init];
38
+
39
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init] eventEmitter:nil childViewControllers:@[vc1, vc2]];
40
+    XCTAssertTrue(uut.viewControllers.count == 2);
43 41
 }
44 42
 
45 43
 - (void)testInitWithLayoutInfo_shouldInitializeDependencies {
46
-	RNNLayoutInfo* layoutInfo = [RNNLayoutInfo new];
47
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{}];
48
-	RNNTabBarPresenter* presenter = [[RNNTabBarPresenter alloc] init];
49
-	NSArray* childViewControllers = @[[UIViewController new]];
50
-	
51
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:childViewControllers];
52
-	XCTAssertTrue(uut.layoutInfo == layoutInfo);
53
-	XCTAssertTrue(uut.options == options);
54
-	XCTAssertTrue(uut.presenter == presenter);
55
-	XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
44
+    RNNLayoutInfo *layoutInfo = [RNNLayoutInfo new];
45
+    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
46
+    RNNTabBarPresenter *presenter = [[RNNTabBarPresenter alloc] init];
47
+    NSArray *childViewControllers = @[[UIViewController new]];
48
+
49
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:childViewControllers];
50
+    XCTAssertTrue(uut.layoutInfo == layoutInfo);
51
+    XCTAssertTrue(uut.options == options);
52
+    XCTAssertTrue(uut.presenter == presenter);
53
+    XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
56 54
 }
57 55
 
58 56
 - (void)testInitWithEventEmmiter_shouldInitializeDependencies {
59
-	RNNLayoutInfo* layoutInfo = [RNNLayoutInfo new];
60
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{}];
61
-	RNNTabBarPresenter* presenter = [[RNNTabBarPresenter alloc] init];
62
-	RNNEventEmitter* eventEmmiter = [RNNEventEmitter new];
57
+    RNNLayoutInfo *layoutInfo = [RNNLayoutInfo new];
58
+    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
59
+    RNNTabBarPresenter *presenter = [[RNNTabBarPresenter alloc] init];
60
+    RNNEventEmitter *eventEmmiter = [RNNEventEmitter new];
63 61
 
64
-	NSArray* childViewControllers = @[[UIViewController new]];
65
-	
66
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:eventEmmiter childViewControllers:childViewControllers];
67
-	XCTAssertTrue(uut.layoutInfo == layoutInfo);
68
-	XCTAssertTrue(uut.options == options);
69
-	XCTAssertTrue(uut.presenter == presenter);
70
-	XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
71
-	XCTAssertTrue(uut.eventEmitter == eventEmmiter);
62
+    NSArray *childViewControllers = @[[UIViewController new]];
63
+
64
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo creator:nil options:options defaultOptions:nil presenter:presenter eventEmitter:eventEmmiter childViewControllers:childViewControllers];
65
+    XCTAssertTrue(uut.layoutInfo == layoutInfo);
66
+    XCTAssertTrue(uut.options == options);
67
+    XCTAssertTrue(uut.presenter == presenter);
68
+    XCTAssertTrue(uut.childViewControllers.count == childViewControllers.count);
69
+    XCTAssertTrue(uut.eventEmitter == eventEmmiter);
72 70
 }
73 71
 
74 72
 - (void)testInitWithLayoutInfo_shouldSetDelegate {
75
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNBasePresenter alloc] init] eventEmitter:nil childViewControllers:nil];
76
-	
77
-	XCTAssertTrue(uut.delegate == uut);
73
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNBasePresenter alloc] init] eventEmitter:nil childViewControllers:nil];
74
+
75
+    XCTAssertTrue(uut.delegate == uut);
78 76
 }
79 77
 
80 78
 - (void)testWillMoveToParent_shouldNotInvokePresenterApplyOptionsOnWillMoveToNilParent {
81
-	[[self.mockTabBarPresenter reject] applyOptionsOnWillMoveToParentViewController:[(RNNTabBarController *)self.mockUut options]];
82
-	[self.mockUut willMoveToParentViewController:nil];
83
-	[self.mockTabBarPresenter verify];
79
+    [[self.mockTabBarPresenter reject] applyOptionsOnWillMoveToParentViewController:[self.uut options]];
80
+    [self.uut willMoveToParentViewController:nil];
81
+    [self.mockTabBarPresenter verify];
84 82
 }
85 83
 
86 84
 - (void)testOnChildAppear_shouldInvokePresenterApplyOptionsWithResolvedOptions {
87
-	[[self.mockTabBarPresenter expect] applyOptions:[OCMArg any]];
88
-	[self.mockUut onChildWillAppear];
89
-	[self.mockTabBarPresenter verify];
85
+    [[self.mockTabBarPresenter expect] applyOptions:[OCMArg any]];
86
+    [self.uut onChildWillAppear];
87
+    [self.mockTabBarPresenter verify];
90 88
 }
91 89
 
92 90
 - (void)testMergeOptions_shouldInvokePresenterMergeOptions {
93
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{}];
94
-	
95
-	[(RNNTabBarPresenter *)[self.mockTabBarPresenter expect] mergeOptions:options currentOptions:[(RNNTabBarController *)self.mockUut options] defaultOptions:nil];
96
-	[(RNNTabBarController *)self.mockUut mergeOptions:options];
97
-	[self.mockTabBarPresenter verify];
91
+    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
92
+
93
+    [(RNNTabBarPresenter *) [self.mockTabBarPresenter expect] mergeOptions:options currentOptions:[self.uut options] defaultOptions:nil];
94
+    [self.uut mergeOptions:options];
95
+    [self.mockTabBarPresenter verify];
98 96
 }
99 97
 
100 98
 - (void)testMergeOptions_shouldInvokeParentMergeOptions {
101
-	id parentMock = [OCMockObject partialMockForObject:[RNNRootViewController new]];
102
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{}];
99
+    id parentMock = [OCMockObject partialMockForObject:[RNNRootViewController new]];
100
+    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:@{}];
103 101
 
104
-	OCMStub([self.mockUut parentViewController]).andReturn(parentMock);
105
-	[((RNNRootViewController *)[parentMock expect]) mergeOptions:options];
106
-	[(RNNTabBarController *)self.mockUut mergeOptions:options];
107
-	[parentMock verify];
102
+    OCMStub([self.uut parentViewController]).andReturn(parentMock);
103
+    [((RNNRootViewController *) [parentMock expect]) mergeOptions:options];
104
+    [self.uut mergeOptions:options];
105
+    [parentMock verify];
108 106
 }
109 107
 
110 108
 - (void)testOnChildAppear_shouldInvokeParentOnChildAppear {
111
-	id parentMock = [OCMockObject partialMockForObject:[RNNNavigationController new]];
109
+    id parentMock = [OCMockObject partialMockForObject:[RNNNavigationController new]];
112 110
 
113
-	OCMStub([self.mockUut parentViewController]).andReturn(parentMock);
114
-	
115
-	[[parentMock expect] onChildWillAppear];
116
-	[self.mockUut onChildWillAppear];
117
-	[parentMock verify];
111
+    OCMStub([self.uut parentViewController]).andReturn(parentMock);
112
+
113
+    [[parentMock expect] onChildWillAppear];
114
+    [self.uut onChildWillAppear];
115
+    [parentMock verify];
116
+}
117
+
118
+- (void)testViewDidLayoutSubviews_delegateToPresenter {
119
+    [[[self mockTabBarPresenter] expect] viewDidLayoutSubviews];
120
+    [[self uut] viewDidLayoutSubviews];
121
+    [[self mockTabBarPresenter] verify];
118 122
 }
119 123
 
120 124
 - (void)testGetCurrentChild_shouldReturnSelectedViewController {
121
-	XCTAssertEqual([self.mockUut getCurrentChild], [(RNNTabBarController *)self.mockUut selectedViewController]);
125
+    XCTAssertEqual([self.uut getCurrentChild], [(RNNTabBarController *) self.uut selectedViewController]);
122 126
 }
123 127
 
124 128
 - (void)testPreferredStatusBarStyle_shouldInvokeSelectedViewControllerPreferredStatusBarStyle {
125
-	[[self.mockChildViewController expect] preferredStatusBarStyle];
126
-	[self.mockUut preferredStatusBarStyle];
127
-	[self.mockChildViewController verify];
129
+    [[self.mockChildViewController expect] preferredStatusBarStyle];
130
+    [self.uut preferredStatusBarStyle];
131
+    [self.mockChildViewController verify];
128 132
 }
129 133
 
130 134
 - (void)testPreferredStatusBarStyle_shouldInvokeOnSelectedViewController {
131
-	[[self.mockChildViewController expect] preferredStatusBarStyle];
132
-	[self.mockUut preferredStatusBarStyle];
133
-	[self.mockChildViewController verify];
135
+    [[self.mockChildViewController expect] preferredStatusBarStyle];
136
+    [self.uut preferredStatusBarStyle];
137
+    [self.mockChildViewController verify];
134 138
 }
135 139
 
136 140
 - (void)testTabBarControllerDidSelectViewControllerDelegate_shouldInvokeSendBottomTabSelectedEvent {
137
-	NSUInteger selectedIndex = 2;
138
-	OCMStub([self.mockUut selectedIndex]).andReturn(selectedIndex);
141
+    NSUInteger selectedIndex = 2;
142
+    OCMStub([self.uut selectedIndex]).andReturn(selectedIndex);
139 143
 
140
-	[[self.mockEventEmmiter expect] sendBottomTabSelected:@(selectedIndex) unselected:@(0)];
141
-	[self.mockUut tabBarController:self.mockUut didSelectViewController:[UIViewController new]];
142
-	[self.mockEventEmmiter verify];
144
+    [[self.mockEventEmitter expect] sendBottomTabSelected:@(selectedIndex) unselected:@(0)];
145
+    [self.uut tabBarController:self.uut didSelectViewController:[UIViewController new]];
146
+    [self.mockEventEmitter verify];
143 147
 }
144 148
 
145 149
 - (void)testSetSelectedIndexByComponentID_ShouldSetSelectedIndexWithCorrectIndex {
146
-	RNNLayoutInfo* layoutInfo = [RNNLayoutInfo new];
147
-	layoutInfo.componentId = @"componentId";
148
-	
149
-	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
150
-	
151
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:[RNNTabBarPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
152
-	[uut setSelectedIndexByComponentID:@"componentId"];
153
-	XCTAssertTrue(uut.selectedIndex == 1);
150
+    RNNLayoutInfo *layoutInfo = [RNNLayoutInfo new];
151
+    layoutInfo.componentId = @"componentId";
152
+
153
+    RNNRootViewController *vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
154
+
155
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:[RNNTabBarPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
156
+    [uut setSelectedIndexByComponentID:@"componentId"];
157
+    XCTAssertTrue(uut.selectedIndex == 1);
154 158
 }
155 159
 
156 160
 - (void)testSetSelectedIndex_ShouldSetSelectedIndexWithCurrentTabIndex {
157
-	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initEmptyOptions];
158
-	options.bottomTabs.currentTabIndex = [[IntNumber alloc] initWithValue:@(1)];
161
+    RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initEmptyOptions];
162
+    options.bottomTabs.currentTabIndex = [[IntNumber alloc] initWithValue:@(1)];
163
+
164
+    RNNRootViewController *vc = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
165
+    RNNTabBarController *uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:nil presenter:[RNNTabBarPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
159 166
 
160
-	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:nil options:nil defaultOptions:nil];
161
-	RNNTabBarController* uut = [[RNNTabBarController alloc] initWithLayoutInfo:nil creator:nil options:options defaultOptions:nil presenter:[RNNTabBarPresenter new] eventEmitter:nil childViewControllers:@[[UIViewController new], vc]];
162
-	
163
-	XCTAssertTrue(uut.selectedIndex == 1);
167
+    XCTAssertTrue(uut.selectedIndex == 1);
164 168
 }
165 169
 
166 170
 @end

+ 54
- 33
lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m View File

@@ -6,9 +6,9 @@
6 6
 
7 7
 @interface RNNTabBarPresenterTest : XCTestCase
8 8
 
9
-@property (nonatomic, strong) RNNTabBarPresenter *uut;
10
-@property (nonatomic, strong) RNNNavigationOptions *options;
11
-@property (nonatomic, strong) id bindedViewController;
9
+@property(nonatomic, strong) RNNTabBarPresenter *uut;
10
+@property(nonatomic, strong) RNNNavigationOptions *options;
11
+@property(nonatomic, strong) id boundViewController;
12 12
 
13 13
 @end
14 14
 
@@ -16,43 +16,64 @@
16 16
 
17 17
 - (void)setUp {
18 18
     [super setUp];
19
-	self.uut = [[RNNTabBarPresenter alloc] init];
20
-	self.bindedViewController = [OCMockObject partialMockForObject:[RNNTabBarController new]];
21
-	[self.uut bindViewController:self.bindedViewController];
22
-	self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
19
+    self.uut = [OCMockObject partialMockForObject:[RNNTabBarPresenter new]];
20
+    self.boundViewController = [OCMockObject partialMockForObject:[RNNTabBarController new]];
21
+    [self.uut bindViewController:self.boundViewController];
22
+    self.options = [[RNNNavigationOptions alloc] initEmptyOptions];
23 23
 }
24 24
 
25 25
 - (void)testApplyOptions_shouldSetDefaultEmptyOptions {
26
-	RNNNavigationOptions* emptyOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
27
-	[[self.bindedViewController expect] rnn_setTabBarTestID:nil];
28
-	[[self.bindedViewController expect] rnn_setTabBarBackgroundColor:nil];
29
-	[[self.bindedViewController expect] rnn_setTabBarTranslucent:NO];
30
-	[[self.bindedViewController expect] rnn_setTabBarHideShadow:NO];
31
-    [[self.bindedViewController expect] rnn_setTabBarStyle:UIBarStyleDefault];
32
-	[[self.bindedViewController expect] rnn_setTabBarVisible:YES animated:NO];
33
-	[self.uut applyOptions:emptyOptions];
34
-	[self.bindedViewController verify];
26
+    RNNNavigationOptions *emptyOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
27
+    [[self.boundViewController expect] rnn_setTabBarTestID:nil];
28
+    [[self.boundViewController expect] rnn_setTabBarBackgroundColor:nil];
29
+    [[self.boundViewController expect] rnn_setTabBarTranslucent:NO];
30
+    [[self.boundViewController expect] rnn_setTabBarHideShadow:NO];
31
+    [[self.boundViewController expect] rnn_setTabBarStyle:UIBarStyleDefault];
32
+    [[self.boundViewController expect] rnn_setTabBarVisible:YES animated:NO];
33
+    [self.uut applyOptions:emptyOptions];
34
+    [self.boundViewController verify];
35 35
 }
36 36
 
37 37
 - (void)testApplyOptions_shouldApplyOptions {
38
-	RNNNavigationOptions* initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
39
-	initialOptions.bottomTabs.testID = [[Text alloc] initWithValue:@"testID"];
40
-	initialOptions.bottomTabs.backgroundColor = [[Color alloc] initWithValue:[UIColor redColor]];
41
-	initialOptions.bottomTabs.translucent = [[Bool alloc] initWithValue:@(0)];
42
-	initialOptions.bottomTabs.hideShadow = [[Bool alloc] initWithValue:@(1)];
43
-	initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(0)];
44
-	initialOptions.bottomTabs.barStyle = [[Text alloc] initWithValue:@"black"];
45
-	
46
-	[[self.bindedViewController expect] rnn_setTabBarTestID:@"testID"];
47
-	[[self.bindedViewController expect] rnn_setTabBarBackgroundColor:[UIColor redColor]];
48
-	[[self.bindedViewController expect] rnn_setTabBarTranslucent:NO];
49
-	[[self.bindedViewController expect] rnn_setTabBarHideShadow:YES];
50
-	[[self.bindedViewController expect] rnn_setTabBarStyle:UIBarStyleBlack];
51
-	[[self.bindedViewController expect] rnn_setTabBarVisible:NO animated:NO];
52
-	
53
-	[self.uut applyOptions:initialOptions];
54
-	[self.bindedViewController verify];
38
+    RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
39
+    initialOptions.bottomTabs.testID = [[Text alloc] initWithValue:@"testID"];
40
+    initialOptions.bottomTabs.backgroundColor = [[Color alloc] initWithValue:[UIColor redColor]];
41
+    initialOptions.bottomTabs.translucent = [[Bool alloc] initWithValue:@(0)];
42
+    initialOptions.bottomTabs.hideShadow = [[Bool alloc] initWithValue:@(1)];
43
+    initialOptions.bottomTabs.visible = [[Bool alloc] initWithValue:@(0)];
44
+    initialOptions.bottomTabs.barStyle = [[Text alloc] initWithValue:@"black"];
45
+
46
+    [[self.boundViewController expect] rnn_setTabBarTestID:@"testID"];
47
+    [[self.boundViewController expect] rnn_setTabBarBackgroundColor:[UIColor redColor]];
48
+    [[self.boundViewController expect] rnn_setTabBarTranslucent:NO];
49
+    [[self.boundViewController expect] rnn_setTabBarHideShadow:YES];
50
+    [[self.boundViewController expect] rnn_setTabBarStyle:UIBarStyleBlack];
51
+    [[self.boundViewController expect] rnn_setTabBarVisible:NO animated:NO];
52
+
53
+    [self.uut applyOptions:initialOptions];
54
+    [self.boundViewController verify];
55
+}
56
+
57
+- (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
58
+    id uut = [self uut];
59
+    [[uut expect] applyDotIndicator];
60
+    [uut viewDidLayoutSubviews];
61
+    [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
62
+    [uut verify];
55 63
 }
56 64
 
65
+- (void)testApplyDotIndicator_callsAppliesBadgeWithEachChild {
66
+    id uut = [self uut];
67
+    id child1 = [UIViewController new];
68
+    id child2 = [UIViewController new];
69
+
70
+    [[uut expect] applyDotIndicator:child1];
71
+    [[uut expect] applyDotIndicator:child2];
72
+    [[self boundViewController] addChildViewController:child1];
73
+    [[self boundViewController] addChildViewController:child2];
74
+
75
+    [uut applyDotIndicator];
76
+    [uut verify];
77
+}
57 78
 
58 79
 @end

+ 4
- 4
lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m View File

@@ -141,7 +141,7 @@
141 141
 	
142 142
 	self.options.topBar.title.component = [[RNNComponentOptions alloc] initWithDict:@{@"name": @"componentName"}];
143 143
 	
144
-	[[(id)self.componentRegistry expect] clearComponentsForParentId:self.uut.bindedComponentId];
144
+	[[(id)self.componentRegistry expect] clearComponentsForParentId:self.uut.boundComponentId];
145 145
 	self.uut = nil;
146 146
 	[(id)self.componentRegistry verify];
147 147
 }
@@ -153,7 +153,7 @@
153 153
 	bindViewController.layoutInfo = layoutInfo;
154 154
 	
155 155
 	[self.uut bindViewController:bindViewController];
156
-	XCTAssertEqual(self.uut.bindedComponentId, @"componentId");
156
+	XCTAssertEqual(self.uut.boundComponentId, @"componentId");
157 157
 }
158 158
 
159 159
 - (void)testRenderComponentsCreateReactViewWithBindedComponentId {
@@ -165,12 +165,12 @@
165 165
 	
166 166
 	self.options.topBar.title.component = [[RNNComponentOptions alloc] initWithDict:@{@"name": @"titleComponent"}];
167 167
 	
168
-	[[(id)self.componentRegistry expect] createComponentIfNotExists:self.options.topBar.title.component parentComponentId:self.uut.bindedComponentId reactViewReadyBlock:[OCMArg any]];
168
+	[[(id)self.componentRegistry expect] createComponentIfNotExists:self.options.topBar.title.component parentComponentId:self.uut.boundComponentId reactViewReadyBlock:[OCMArg any]];
169 169
 	[self.uut renderComponents:self.options perform:nil];
170 170
 	[(id)self.componentRegistry verify];
171 171
 	
172 172
 	
173
-	XCTAssertEqual(self.uut.bindedComponentId, @"componentId");
173
+	XCTAssertEqual(self.uut.boundComponentId, @"componentId");
174 174
 }
175 175
 
176 176
 - (void)testApplyOptionsOnWillMoveToParent_shouldSetBackButtonOnBindedViewController_withTitle {

+ 2
- 1
lib/ios/ReactNativeNavigationTests/UITabBarController+RNNOptionsTest.m View File

@@ -1,4 +1,5 @@
1 1
 #import <XCTest/XCTest.h>
2
+#import <OCMockObject.h>
2 3
 #import "UITabBarController+RNNOptions.h"
3 4
 
4 5
 @interface UITabBarController_RNNOptionsTest : XCTestCase
@@ -11,7 +12,7 @@
11 12
 
12 13
 - (void)setUp {
13 14
     [super setUp];
14
-	self.uut = [UITabBarController new];
15
+	self.uut = [OCMockObject partialMockForObject:[UITabBarController new]];
15 16
 }
16 17
 
17 18
 - (void)test_tabBarTranslucent_true {

+ 26
- 24
lib/ios/ReactNativeNavigationTests/UIViewController+RNNOptionsTest.m View File

@@ -1,10 +1,11 @@
1 1
 #import <XCTest/XCTest.h>
2 2
 #import "UIViewController+RNNOptions.h"
3 3
 #import <OCMock/OCMock.h>
4
+#import "RNNBottomTabOptions.h"
4 5
 
5 6
 @interface UIViewController_RNNOptionsTest : XCTestCase
6 7
 
7
-@property (nonatomic, retain) id uut;
8
+@property(nonatomic, retain) id uut;
8 9
 
9 10
 @end
10 11
 
@@ -12,27 +13,28 @@
12 13
 
13 14
 - (void)setUp {
14 15
     [super setUp];
15
-	self.uut = [OCMockObject partialMockForObject:[UIViewController new]];
16
+    self.uut = [OCMockObject partialMockForObject:[UIViewController new]];
16 17
 }
17 18
 
18 19
 - (void)test_setTabBarItemBadge_shouldSetValidValue {
19
-	NSString* badgeValue = @"badge";
20
-	[self.uut rnn_setTabBarItemBadge:badgeValue];
21
-	XCTAssertEqual([self.uut tabBarItem].badgeValue, badgeValue);
20
+    NSDictionary *dict = @{@"badge": @"badge"};
21
+    RNNBottomTabOptions * options = [[RNNBottomTabOptions alloc] initWithDict:dict];
22
+    [self.uut rnn_setTabBarItemBadge:options];
23
+    XCTAssertEqual([self.uut tabBarItem].badgeValue, @"badge");
22 24
 }
23 25
 
24 26
 - (void)test_setTabBarItemBadge_shouldResetWhenValueIsEmptyString {
25
-	[self.uut rnn_setTabBarItemBadge:@"badge"];
26
-	NSString* badgeValue = @"";
27
-	[self.uut rnn_setTabBarItemBadge:badgeValue];
28
-	XCTAssertEqual([self.uut tabBarItem].badgeValue, nil);
27
+    [self.uut rnn_setTabBarItemBadge:[[RNNBottomTabOptions alloc] initWithDict:@{@"badge": @"badge"}]];
28
+
29
+    RNNBottomTabOptions * optionsWithEmptyBadge = [[RNNBottomTabOptions alloc] initWithDict:@{@"badge": @""}];
30
+    [self.uut rnn_setTabBarItemBadge:optionsWithEmptyBadge];
31
+    XCTAssertEqual([self.uut tabBarItem].badgeValue, nil);
29 32
 }
30 33
 
31 34
 - (void)test_setTabBarItemBadge_shouldResetWhenValueIsNullObject {
32
-	[self.uut rnn_setTabBarItemBadge:@"badge"];
33
-	NSNull* nullBadgeValue = [NSNull new];
34
-	[self.uut rnn_setTabBarItemBadge:nullBadgeValue];
35
-	XCTAssertEqual([self.uut tabBarItem].badgeValue, nil);
35
+    [self.uut rnn_setTabBarItemBadge:[[RNNBottomTabOptions alloc] initWithDict:@{@"badge": @"badge"}]];
36
+    [self.uut rnn_setTabBarItemBadge:[[RNNBottomTabOptions alloc] initWithDict:@{@"badge": [NSNull new]}]];
37
+    XCTAssertEqual([self.uut tabBarItem].badgeValue, nil);
36 38
 }
37 39
 
38 40
 - (void)testSetDrawBehindTopBarTrue_shouldSetExtendedLayoutTrue {
@@ -60,33 +62,33 @@
60 62
 }
61 63
 
62 64
 - (void)testSetDrawBehindTopBarFalse_shouldSetCorrectEdgesForExtendedLayout {
63
-	[self.uut rnn_setDrawBehindTopBar:NO];
65
+    [self.uut rnn_setDrawBehindTopBar:NO];
64 66
     UIRectEdge expectedRectEdge = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight;
65 67
     XCTAssertEqual([self.uut edgesForExtendedLayout], expectedRectEdge);
66 68
 }
67 69
 
68 70
 - (void)testSetDrawBehindTapBarFalse_shouldSetCorrectEdgesForExtendedLayout {
69 71
     [self.uut rnn_setDrawBehindTabBar:NO];
70
-	UIRectEdge expectedRectEdge = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
71
-	XCTAssertEqual([self.uut edgesForExtendedLayout], expectedRectEdge);
72
+    UIRectEdge expectedRectEdge = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
73
+    XCTAssertEqual([self.uut edgesForExtendedLayout], expectedRectEdge);
72 74
 }
73 75
 
74 76
 - (void)testSetBackgroundImageShouldNotAddViewIfImageNil {
75
-	NSUInteger subviewsCount = [[[self.uut view] subviews] count];
76
-	[self.uut rnn_setBackgroundImage:nil];
77
-	XCTAssertEqual([[[self.uut view] subviews] count], subviewsCount);
77
+    NSUInteger subviewsCount = [[[self.uut view] subviews] count];
78
+    [self.uut rnn_setBackgroundImage:nil];
79
+    XCTAssertEqual([[[self.uut view] subviews] count], subviewsCount);
78 80
 }
79 81
 
80 82
 - (void)testSetBackgroundImageShouldAddUIImageViewSubview {
81
-	NSUInteger subviewsCount = [[[self.uut view] subviews] count];
82
-	[self.uut rnn_setBackgroundImage:[UIImage new]];
83
-	XCTAssertEqual([[[self.uut view] subviews] count], subviewsCount+1);
83
+    NSUInteger subviewsCount = [[[self.uut view] subviews] count];
84
+    [self.uut rnn_setBackgroundImage:[UIImage new]];
85
+    XCTAssertEqual([[[self.uut view] subviews] count], subviewsCount + 1);
84 86
 }
85 87
 
86 88
 - (void)testSetBackgroundImageShouldAddUIImageViewSubviewWithImage {
87
-    UIImage* image = [UIImage new];
89
+    UIImage *image = [UIImage new];
88 90
     [self.uut rnn_setBackgroundImage:image];
89
-    UIImageView* imageView = [[[self.uut view] subviews] firstObject];
91
+    UIImageView *imageView = [[[self.uut view] subviews] firstObject];
90 92
     XCTAssertEqual(imageView.image, image);
91 93
 }
92 94
 

+ 9
- 0
lib/ios/ReactNativeNavigationTests/utils/RNNTestBase.h View File

@@ -0,0 +1,9 @@
1
+#import <Foundation/Foundation.h>
2
+#import <XCTest/XCTest.h>
3
+
4
+
5
+@interface RNNTestBase : XCTestCase
6
+- (void)setupTopLevelUI:(UIViewController *)withViewController;
7
+
8
+- (void)tearDownTopLevelUI:(UIViewController *)withViewController;
9
+@end

+ 19
- 0
lib/ios/ReactNativeNavigationTests/utils/RNNTestBase.m View File

@@ -0,0 +1,19 @@
1
+#import "RNNTestBase.h"
2
+
3
+@interface RNNTestBase ()
4
+@property(nonatomic, strong) UIWindow *window;
5
+@end
6
+
7
+@implementation RNNTestBase
8
+
9
+- (void)setupTopLevelUI:(UIViewController *)withViewController {
10
+    [withViewController viewDidLoad];
11
+    [withViewController viewWillAppear:YES];
12
+    [withViewController viewDidAppear:YES];
13
+}
14
+
15
+- (void)tearDownTopLevelUI:(UIViewController *)withViewController {
16
+    [withViewController viewWillDisappear:YES];
17
+    [withViewController viewDidDisappear:YES];
18
+}
19
+@end

+ 10
- 0
lib/ios/UITabBarController+RNNOptions.m View File

@@ -74,4 +74,14 @@
74 74
 	}
75 75
 }
76 76
 
77
+- (void)rnn_forEachTab:(void (^)(UIView *, UIViewController * tabViewController, int tabIndex))performOnTab {
78
+    int tabIndex = 0;
79
+    for (UIView * tab in self.tabBar.subviews) {
80
+        if ([NSStringFromClass([tab class]) isEqualToString:@"UITabBarButton"]) {
81
+            performOnTab(tab, [self childViewControllers][(NSUInteger) tabIndex], tabIndex);
82
+            tabIndex++;
83
+        }
84
+    }
85
+}
86
+
77 87
 @end

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

@@ -14,6 +14,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
14 14
 
15 15
 - (RNNNavigationOptions *)resolveOptions;
16 16
 
17
+- (RNNNavigationOptions *)resolveChildOptions:(UIViewController *) child;
18
+
17 19
 - (void)setDefaultOptions:(RNNNavigationOptions *)defaultOptions;
18 20
 
19 21
 - (void)overrideOptions:(RNNNavigationOptions *)options;

+ 5
- 1
lib/ios/UIViewController+LayoutProtocol.m View File

@@ -33,9 +33,13 @@
33 33
 	return [(RNNNavigationOptions *)[self.options mergeInOptions:self.getCurrentChild.resolveOptions.copy] withDefault:self.defaultOptions];
34 34
 }
35 35
 
36
+- (RNNNavigationOptions *)resolveChildOptions:(UIViewController *) child {
37
+	return [(RNNNavigationOptions *)[self.options mergeInOptions:child.resolveOptions.copy] withDefault:self.defaultOptions];
38
+}
39
+
36 40
 - (void)mergeOptions:(RNNNavigationOptions *)options {
37 41
 	[self.presenter mergeOptions:options currentOptions:self.options defaultOptions:self.defaultOptions];
38
-	[((UIViewController *)self.parentViewController) mergeOptions:options];
42
+	[self.parentViewController mergeOptions:options];
39 43
 }
40 44
 
41 45
 - (void)overrideOptions:(RNNNavigationOptions *)options {

+ 3
- 1
lib/ios/UIViewController+RNNOptions.h View File

@@ -1,5 +1,7 @@
1 1
 #import <UIKit/UIKit.h>
2 2
 
3
+@class RNNBottomTabOptions;
4
+
3 5
 @interface UIViewController (RNNOptions)
4 6
 
5 7
 - (void)rnn_setBackgroundImage:(UIImage *)backgroundImage;
@@ -18,7 +20,7 @@
18 20
 
19 21
 - (void)rnn_setTabBarItemBadgeColor:(UIColor *)badgeColor;
20 22
 
21
-- (void)rnn_setTabBarItemBadge:(NSString *)badge;
23
+- (void)rnn_setTabBarItemBadge:(RNNBottomTabOptions *)bottomTab;
22 24
 
23 25
 - (void)rnn_setTopBarPrefersLargeTitle:(BOOL)prefersLargeTitle;
24 26
 

+ 12
- 8
lib/ios/UIViewController+RNNOptions.m View File

@@ -1,6 +1,7 @@
1 1
 #import "UIViewController+RNNOptions.h"
2 2
 #import <React/RCTRootView.h>
3 3
 #import "UIImage+tint.h"
4
+#import "RNNBottomTabOptions.h"
4 5
 
5 6
 #define kStatusBarAnimationDuration 0.35
6 7
 const NSInteger BLUR_STATUS_TAG = 78264801;
@@ -80,14 +81,17 @@ const NSInteger BLUR_STATUS_TAG = 78264801;
80 81
 	}
81 82
 }
82 83
 
83
-- (void)rnn_setTabBarItemBadge:(NSString *)badge {
84
-	UITabBarItem *tabBarItem = self.tabBarItem;
85
-	
86
-	if ([badge isKindOfClass:[NSNull class]] || [badge isEqualToString:@""]) {
87
-		tabBarItem.badgeValue = nil;
88
-	} else {
89
-		tabBarItem.badgeValue = badge;
90
-	}
84
+- (void)rnn_setTabBarItemBadge:(RNNBottomTabOptions *)bottomTab {
85
+    UITabBarItem *tabBarItem = self.tabBarItem;
86
+
87
+    NSString *badge = [bottomTab.badge get];
88
+    if ([badge isKindOfClass:[NSNull class]] || [badge isEqualToString:@""]) {
89
+        tabBarItem.badgeValue = nil;
90
+    } else {
91
+        tabBarItem.badgeValue = badge;
92
+        [[self.tabBarController.tabBar viewWithTag:tabBarItem.tag] removeFromSuperview];
93
+        tabBarItem.tag = -1;
94
+    }
91 95
 }
92 96
 
93 97
 - (void)rnn_setTabBarItemBadgeColor:(UIColor *)badgeColor {

+ 5
- 0
lib/ios/Utils/UIColor+RNNUtils.h View File

@@ -0,0 +1,5 @@
1
+#import <UIKit/UIKit.h>
2
+
3
+@interface UIColor (RNNUtils)
4
+- (NSString *)toHex;
5
+@end

+ 17
- 0
lib/ios/Utils/UIColor+RNNUtils.m View File

@@ -0,0 +1,17 @@
1
+#import "UIColor+RNNUtils.h"
2
+
3
+@implementation UIColor (RNNUtils)
4
+- (NSString *)toHex {
5
+    const CGFloat *components = CGColorGetComponents([self CGColor]);
6
+
7
+    CGFloat r = components[0];
8
+    CGFloat g = components[1];
9
+    CGFloat b = components[2];
10
+
11
+    return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
12
+                                      lroundf((float) (r * 255)),
13
+                                      lroundf((float) (g * 255)),
14
+                                      lroundf((float) (b * 255))];
15
+}
16
+
17
+@end

+ 8
- 0
lib/ios/Utils/UITabBarController+RNNUtils.h View File

@@ -0,0 +1,8 @@
1
+#import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
3
+
4
+@interface UITabBarController (RNNUtils)
5
+- (UIView *)getTabView:(int)tabIndex;
6
+
7
+- (UIView *)getTabIcon:(int)tabIndex;
8
+@end

+ 21
- 0
lib/ios/Utils/UITabBarController+RNNUtils.m View File

@@ -0,0 +1,21 @@
1
+#import "UITabBarController+RNNUtils.h"
2
+#import "UIView+Utils.h"
3
+
4
+
5
+@implementation UITabBarController (RNNUtils)
6
+- (UIView *)getTabView:(int)tabIndex {
7
+    int index = 0;
8
+    for (UIView *view in [[self tabBar] subviews]) {
9
+        if ([NSStringFromClass([view class]) isEqualToString:@"UITabBarButton"]) {
10
+            if (index == tabIndex) return view;
11
+            index++;
12
+        }
13
+    }
14
+    return nil;
15
+}
16
+
17
+- (UIView *)getTabIcon:(int)tabIndex {
18
+    UIView *tab = [self getTabView:tabIndex];
19
+    return [tab findChildByClass:[UIImageView class]];
20
+}
21
+@end

+ 6
- 0
lib/ios/Utils/UIView+Utils.h View File

@@ -0,0 +1,6 @@
1
+#import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
3
+
4
+@interface UIView (Utils)
5
+- (UIView *)findChildByClass:clazz;
6
+@end

+ 12
- 0
lib/ios/Utils/UIView+Utils.m View File

@@ -0,0 +1,12 @@
1
+#import "UIView+Utils.h"
2
+
3
+
4
+@implementation UIView (Utils)
5
+- (UIView *)findChildByClass:(id)clazz {
6
+    for (UIView *child in [self subviews]) {
7
+        if ([child isKindOfClass:clazz]) return child;
8
+    }
9
+    return nil;
10
+}
11
+
12
+@end

+ 6
- 0
lib/ios/Utils/UIViewController+Utils.h View File

@@ -0,0 +1,6 @@
1
+#import <Foundation/Foundation.h>
2
+#import <UIKit/UIKit.h>
3
+
4
+@interface UIViewController (Utils)
5
+- (void)forEachChild:(void (^)(UIViewController *child))perform;
6
+@end

+ 10
- 0
lib/ios/Utils/UIViewController+Utils.m View File

@@ -0,0 +1,10 @@
1
+#import "UIViewController+Utils.h"
2
+
3
+@implementation UIViewController (Utils)
4
+- (void)forEachChild:(void (^)(UIViewController *child))perform {
5
+    for (UIViewController *child in [self childViewControllers]) {
6
+        perform(child);
7
+    }
8
+}
9
+
10
+@end

+ 31
- 5
playground/src/screens/FirstBottomTabScreen.js View File

@@ -17,6 +17,9 @@ const {
17 17
 class FirstBottomTabScreen extends React.Component {
18 18
   static options() {
19 19
     return {
20
+      layout: {
21
+        orientation: ['portrait', 'landscape']
22
+      },
20 23
       topBar: {
21 24
         title: {
22 25
           text: 'First Tab'
@@ -24,21 +27,29 @@ class FirstBottomTabScreen extends React.Component {
24 27
       },
25 28
       bottomTab: {
26 29
         icon: require('../../img/whatshot.png'),
27
-        text: 'Tab 1'
30
+        text: 'Tab 1',
31
+        dotIndicator: { visible: true }
28 32
       }
29 33
     };
30 34
   }
31 35
 
36
+  constructor(props) {
37
+    super(props);
38
+    this.dotVisible = true;
39
+  }
40
+
32 41
   render() {
33 42
     return (
34 43
       <Root componentId={this.props.componentId}>
35 44
         <Button label='Switch Tab by Index' testID={SWITCH_TAB_BY_INDEX_BTN} onPress={this.switchTabByIndex} />
36 45
         <Button label='Switch Tab by componentId' testID={SWITCH_TAB_BY_COMPONENT_ID_BTN} onPress={this.switchTabByComponentId} />
37 46
         <Button label='Set Badge' testID={SET_BADGE_BTN} onPress={() => this.setBadge('NEW')} />
38
-        <Button label='Clear Badge' testID={CLEAR_BADGE_BTN} onPress={() => this.setBadge(null)} />
47
+        <Button label='Clear Badge' testID={CLEAR_BADGE_BTN} onPress={() => this.setBadge('')} />
48
+        <Button label='Set Notification Dot' onPress={this.setNotificationDot} />
39 49
         <Button label='Hide Tabs' testID={HIDE_TABS_BTN} onPress={() => this.toggleTabs(false)} />
40 50
         <Button label='Show Tabs' testID={SHOW_TABS_BTN} onPress={() => this.toggleTabs(true)} />
41 51
         <Button label='Hide Tabs on Push' testID={HIDE_TABS_PUSH_BTN} onPress={this.hideTabsOnPush} />
52
+        <Button label='Push' onPress={this.push} />
42 53
       </Root>
43 54
     );
44 55
   }
@@ -55,9 +66,22 @@ class FirstBottomTabScreen extends React.Component {
55 66
     }
56 67
   });
57 68
 
58
-  setBadge = (badge) => Navigation.mergeOptions(this, {
59
-    bottomTab: { badge }
60
-  });
69
+  setBadge = (badge) => {
70
+    this.badgeVisible = !!badge;
71
+    if (this.badgeVisible) this.dotVisible = false;
72
+    Navigation.mergeOptions(this, {
73
+      bottomTab: { badge }
74
+    });
75
+  }
76
+
77
+  setNotificationDot = () => {
78
+    this.dotVisible = !this.dotVisible;
79
+    Navigation.mergeOptions(this, {
80
+      bottomTab: {
81
+        dotIndicator: { visible: this.dotVisible }
82
+      }
83
+    });
84
+  }
61 85
 
62 86
   toggleTabs = (visible) => Navigation.mergeOptions(this, {
63 87
     bottomTabs: { visible }
@@ -66,6 +90,8 @@ class FirstBottomTabScreen extends React.Component {
66 90
   hideTabsOnPush = () => Navigation.push(this, component(Screens.Pushed, {
67 91
     bottomTabs: { visible: false }
68 92
   }));
93
+
94
+  push = () => Navigation.push(this, Screens.Pushed);
69 95
 }
70 96
 
71 97
 module.exports = FirstBottomTabScreen;

+ 8
- 1
playground/src/screens/SecondBottomTabScreen.js View File

@@ -22,7 +22,11 @@ class SecondBottomTabScreen extends React.Component {
22 22
       },
23 23
       bottomTab: {
24 24
         icon: require('../../img/star.png'),
25
-        text: 'Tab 2'
25
+        text: 'Tab 2',
26
+        dotIndicator: {
27
+          visible: true,
28
+          color: 'green'
29
+        }
26 30
       }
27 31
     };
28 32
   }
@@ -30,12 +34,15 @@ class SecondBottomTabScreen extends React.Component {
30 34
   render() {
31 35
     return (
32 36
       <Root componentId={this.props.componentId}>
37
+        <Button label='Push' onPress={this.push} />
33 38
         <Button label='Push BottomTabs' testID={PUSH_BTN} onPress={this.pushBottomTabs} />
34 39
         <Button label='SideMenu inside BottomTabs' testID={SIDE_MENU_INSIDE_BOTTOM_TABS_BTN} onPress={this.sideMenuInsideBottomTabs} />
35 40
       </Root>
36 41
     );
37 42
   }
38 43
 
44
+  push = () => Navigation.push(this, Screens.Pushed);
45
+
39 46
   pushBottomTabs = () => Navigation.push(this, {
40 47
     bottomTabs: {
41 48
       children: [

+ 2
- 2
scripts/test-unit.js View File

@@ -32,7 +32,7 @@ function runIosUnitTests() {
32 32
             -project playground.xcodeproj
33 33
             -sdk iphonesimulator
34 34
             -configuration ${conf}
35
-            -derivedDataPath ./DerivedData/playground
35
+            -derivedDataPath ./playground/ios/DerivedData/playground
36 36
             -quiet
37 37
             -UseModernBuildSystem=NO
38 38
             ONLY_ACTIVE_ARCH=YES`);
@@ -45,7 +45,7 @@ function runIosUnitTests() {
45 45
             -sdk iphonesimulator
46 46
             -configuration ${conf}
47 47
             -destination 'platform=iOS Simulator,name=iPhone X'
48
-            -derivedDataPath ./DerivedData/playground
48
+            -derivedDataPath ./playground/ios/DerivedData/playground
49 49
             ONLY_ACTIVE_ARCH=YES`);
50 50
 }
51 51