Daniel Zlotin 6 years ago
parent
commit
8d6b0a7881
66 changed files with 843 additions and 374 deletions
  1. 4
    1
      e2e/Orientations.test.js
  2. 7
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  3. 88
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java
  4. 30
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarBackground.java
  5. 22
    71
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  6. 9
    15
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  7. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java
  8. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java
  9. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java
  10. 13
    10
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java
  11. 38
    0
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  12. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java
  13. 6
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java
  14. 4
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  15. 3
    3
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  16. 14
    12
      lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java
  17. 17
    4
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  18. 0
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBarButtonCreator.java
  19. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBarReactButtonView.java
  20. 36
    7
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  21. 23
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBarReactView.java
  22. 20
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBarReactViewCreator.java
  23. 20
    0
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TitleBarReactViewCreatorMock.java
  24. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopBarButtonCreatorMock.java
  25. 31
    21
      lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java
  26. 8
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java
  27. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ComponentViewControllerTest.java
  28. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/FloatingActionButtonTest.java
  29. 7
    6
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java
  30. 16
    15
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  31. 13
    8
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java
  32. 15
    11
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java
  33. 46
    2
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  34. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  35. 17
    11
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  36. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ViewControllerTest.java
  37. 3
    2
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java
  38. 6
    0
      lib/ios/RNNBottomTabsOptions.m
  39. 5
    2
      lib/ios/RNNCustomTitleView.h
  40. 41
    33
      lib/ios/RNNCustomTitleView.m
  41. 6
    0
      lib/ios/RNNNavigationButtons.m
  42. 1
    0
      lib/ios/RNNNavigationOptions.h
  43. 3
    3
      lib/ios/RNNNavigationOptions.m
  44. 6
    1
      lib/ios/RNNOptions.m
  45. 25
    6
      lib/ios/RNNRootViewController.m
  46. 13
    0
      lib/ios/RNNTitleOptions.h
  47. 32
    0
      lib/ios/RNNTitleOptions.m
  48. 8
    6
      lib/ios/RNNTopBarOptions.h
  49. 37
    28
      lib/ios/RNNTopBarOptions.m
  50. 8
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  51. 1
    1
      lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m
  52. 2
    2
      lib/ios/ReactNativeNavigationTests/RNNNavigationOptionsTest.m
  53. 12
    12
      lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m
  54. 4
    2
      lib/src/commands/LayoutTreeParser.test.ts
  55. 3
    1
      playground/src/screens/BackHandlerModalScreen.js
  56. 5
    3
      playground/src/screens/BackHandlerScreen.js
  57. 4
    8
      playground/src/screens/CustomTopBar.js
  58. 4
    2
      playground/src/screens/CustomTransitionDestination.js
  59. 4
    2
      playground/src/screens/CustomTransitionOrigin.js
  60. 33
    18
      playground/src/screens/OptionsScreen.js
  61. 3
    1
      playground/src/screens/PushedScreen.js
  62. 5
    3
      playground/src/screens/ScrollViewScreen.js
  63. 11
    7
      playground/src/screens/TopTabOptionsScreen.js
  64. 5
    3
      playground/src/screens/TopTabScreen.js
  65. 28
    9
      playground/src/screens/WelcomeScreen.js
  66. 1
    0
      playground/src/testIDs.js

+ 4
- 1
e2e/Orientations.test.js View File

@@ -8,14 +8,16 @@ describe('orientation', () => {
8 8
 
9 9
   beforeEach(async () => {
10 10
     await device.relaunchApp();
11
-    waitForDeviceToSettleAfterOrientationChangeAndroid = ms => new Promise(res => setTimeout(res, device.getPlatform() === 'ios' ? 0 : 150));
11
+    waitForDeviceToSettleAfterOrientationChangeAndroid = ms => new Promise(res => setTimeout(res, device.getPlatform() === 'ios' ? 0 : 400));
12 12
   });
13 13
 
14 14
   it('default allows all', async () => {
15 15
     await elementById(testIDs.ORIENTATION_BUTTON).tap();
16 16
     await elementById(testIDs.DEFAULT_ORIENTATION_BUTTON).tap();
17
+    waitForDeviceToSettleAfterOrientationChangeAndroid();
17 18
     await expect(elementById(testIDs.PORTRAIT_ELEMENT)).toBeVisible();
18 19
     await device.setOrientation('landscape');
20
+    waitForDeviceToSettleAfterOrientationChangeAndroid();
19 21
     await expect(elementById(testIDs.LANDSCAPE_ELEMENT)).toBeVisible();
20 22
     await device.setOrientation('portrait');
21 23
     waitForDeviceToSettleAfterOrientationChangeAndroid();
@@ -28,6 +30,7 @@ describe('orientation', () => {
28 30
     await elementById(testIDs.LANDSCAPE_PORTRAIT_ORIENTATION_BUTTON).tap();
29 31
     await expect(element(by.id(testIDs.PORTRAIT_ELEMENT))).toBeVisible();
30 32
     await device.setOrientation('landscape');
33
+    waitForDeviceToSettleAfterOrientationChangeAndroid();
31 34
     await expect(element(by.id(testIDs.LANDSCAPE_ELEMENT))).toBeVisible();
32 35
     await device.setOrientation('portrait');
33 36
     waitForDeviceToSettleAfterOrientationChangeAndroid();

+ 7
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java View File

@@ -15,6 +15,7 @@ import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalCompo
15 15
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
16 16
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
17 17
 import com.reactnativenavigation.views.ComponentViewCreator;
18
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
18 19
 import com.reactnativenavigation.views.TopBarButtonCreator;
19 20
 import com.reactnativenavigation.views.TopTabsLayoutCreator;
20 21
 
@@ -118,7 +119,12 @@ public class LayoutFactory {
118 119
     }
119 120
 
120 121
 	private ViewController createStack(LayoutNode node) {
121
-        StackController stackController = new StackController(activity, new TopBarButtonCreator(reactInstanceManager), node.id, getOptions(node));
122
+        StackController stackController = new StackController(activity,
123
+                new TopBarButtonCreator(reactInstanceManager),
124
+                new TitleBarReactViewCreator(reactInstanceManager),
125
+                node.id,
126
+                getOptions(node)
127
+        );
122 128
         addChildrenToStack(node.children, stackController);
123 129
         return stackController;
124 130
 	}

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

@@ -0,0 +1,88 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import android.graphics.Typeface;
4
+import android.support.annotation.Nullable;
5
+import android.util.Log;
6
+
7
+import com.reactnativenavigation.BuildConfig;
8
+import com.reactnativenavigation.parse.params.Color;
9
+import com.reactnativenavigation.parse.params.Fraction;
10
+import com.reactnativenavigation.parse.params.NullColor;
11
+import com.reactnativenavigation.parse.params.NullFraction;
12
+import com.reactnativenavigation.parse.params.NullText;
13
+import com.reactnativenavigation.parse.params.Text;
14
+import com.reactnativenavigation.parse.parsers.ColorParser;
15
+import com.reactnativenavigation.parse.parsers.FractionParser;
16
+import com.reactnativenavigation.parse.parsers.TextParser;
17
+import com.reactnativenavigation.utils.TypefaceLoader;
18
+
19
+import org.json.JSONObject;
20
+
21
+public class TitleOptions {
22
+    public enum Alignment {
23
+        Center, Fill, Default;
24
+
25
+        public static Alignment fromString(String alignment) {
26
+            switch (alignment) {
27
+                case "center":
28
+                    return Center;
29
+                case "fill":
30
+                    return Fill;
31
+                default:
32
+                    return Default;
33
+            }
34
+        }
35
+    }
36
+
37
+    public static TitleOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
38
+        final TitleOptions options = new TitleOptions();
39
+        if (json == null) {
40
+            return options;
41
+        }
42
+
43
+        options.text = TextParser.parse(json, "text");
44
+        options.color = ColorParser.parse(json, "color");
45
+        options.fontSize = FractionParser.parse(json, "fontSize");
46
+        options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
47
+        options.component = TextParser.parse(json, "component");
48
+        options.alignment = Alignment.fromString(TextParser.parse(json, "alignment").get(""));
49
+
50
+        validate(options);
51
+
52
+        return options;
53
+    }
54
+
55
+    public Text text = new NullText();
56
+    public Color color = new NullColor();
57
+    public Fraction fontSize = new NullFraction();
58
+    @Nullable public Typeface fontFamily;
59
+    public Text component = new NullText();
60
+    public Alignment alignment = Alignment.Default;
61
+
62
+    void mergeWith(final TitleOptions other) {
63
+        if (other.text.hasValue()) text = other.text;
64
+        if (other.color.hasValue()) color = other.color;
65
+        if (other.fontSize.hasValue()) fontSize = other.fontSize;
66
+        if (other.fontFamily != null) fontFamily = other.fontFamily;
67
+        if (other.component.hasValue()) component = other.component;
68
+        if (other.alignment != Alignment.Default) alignment = other.alignment;
69
+        validate(this);
70
+    }
71
+
72
+    void mergeWithDefault(TitleOptions defaultOptions) {
73
+        if (!text.hasValue()) text = defaultOptions.text;
74
+        if (!color.hasValue()) color = defaultOptions.color;
75
+        if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
76
+        if (fontFamily == null) fontFamily = defaultOptions.fontFamily;
77
+        if (!component.hasValue()) component = defaultOptions.component;
78
+        if (alignment == Alignment.Default) alignment = defaultOptions.alignment;
79
+        validate(this);
80
+    }
81
+
82
+    private static void validate(TitleOptions options) {
83
+        if (options.component.hasValue() && options.text.hasValue()) {
84
+            if (BuildConfig.DEBUG) Log.w("RNN", "A screen can't use both text and component - clearing text.");
85
+            options.text = new Text("");
86
+        }
87
+    }
88
+}

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

@@ -0,0 +1,30 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.parse.params.Color;
4
+import com.reactnativenavigation.parse.params.NullColor;
5
+import com.reactnativenavigation.parse.parsers.ColorParser;
6
+
7
+import org.json.JSONObject;
8
+
9
+public class TopBarBackground {
10
+    public static TopBarBackground parse(JSONObject json) {
11
+        TopBarBackground options = new TopBarBackground();
12
+        if (json == null) {
13
+            return options;
14
+        }
15
+
16
+        options.color = ColorParser.parse(json, "color");
17
+
18
+        return options;
19
+    }
20
+
21
+    public Color color = new NullColor();
22
+
23
+    void mergeWith(final TopBarBackground other) {
24
+        if (other.color.hasValue()) color = other.color;
25
+    }
26
+
27
+    void mergeWithDefault(TopBarBackground defaultOptions) {
28
+        if (!color.hasValue()) color = defaultOptions.color;
29
+    }
30
+}

+ 22
- 71
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java View File

@@ -1,21 +1,14 @@
1 1
 package com.reactnativenavigation.parse;
2 2
 
3 3
 
4
-import android.graphics.Typeface;
5 4
 import android.support.annotation.Nullable;
6 5
 
7 6
 import com.reactnativenavigation.parse.params.Bool;
8 7
 import com.reactnativenavigation.parse.params.Button;
9
-import com.reactnativenavigation.parse.params.Color;
10
-import com.reactnativenavigation.parse.params.Fraction;
11 8
 import com.reactnativenavigation.parse.params.NullBool;
12
-import com.reactnativenavigation.parse.params.NullColor;
13
-import com.reactnativenavigation.parse.params.NullFraction;
14 9
 import com.reactnativenavigation.parse.params.NullText;
15 10
 import com.reactnativenavigation.parse.params.Text;
16 11
 import com.reactnativenavigation.parse.parsers.BoolParser;
17
-import com.reactnativenavigation.parse.parsers.ColorParser;
18
-import com.reactnativenavigation.parse.parsers.FractionParser;
19 12
 import com.reactnativenavigation.parse.parsers.TextParser;
20 13
 import com.reactnativenavigation.utils.TypefaceLoader;
21 14
 
@@ -29,11 +22,8 @@ public class TopBarOptions implements DEFAULT_VALUES {
29 22
         TopBarOptions options = new TopBarOptions();
30 23
         if (json == null) return options;
31 24
 
32
-        options.title = TextParser.parse(json, "title");
33
-        options.backgroundColor = ColorParser.parse(json, "backgroundColor");
34
-        options.textColor = ColorParser.parse(json, "textColor");
35
-        options.textFontSize = FractionParser.parse(json, "textFontSize");
36
-        options.textFontFamily = typefaceManager.getTypeFace(json.optString("textFontFamily", ""));
25
+        options.title = TitleOptions.parse(typefaceManager, json.optJSONObject("title"));
26
+        options.background = TopBarBackground.parse(json.optJSONObject("background"));
37 27
         options.visible = BoolParser.parse(json, "visible");
38 28
         options.animate = BoolParser.parse(json,"animate");
39 29
         options.hideOnScroll = BoolParser.parse(json,"hideOnScroll");
@@ -45,12 +35,9 @@ public class TopBarOptions implements DEFAULT_VALUES {
45 35
         return options;
46 36
     }
47 37
 
48
-    public Text title = new NullText();
38
+    public TitleOptions title = new TitleOptions();
49 39
     public Text testId = new NullText();
50
-    public Color backgroundColor = new NullColor();
51
-    public Color textColor = new NullColor();
52
-    public Fraction textFontSize = new NullFraction();
53
-    @Nullable public Typeface textFontFamily;
40
+    public TopBarBackground background = new TopBarBackground();
54 41
     public Bool visible = new NullBool();
55 42
     public Bool animate = new NullBool();
56 43
     public Bool hideOnScroll = new NullBool();
@@ -59,62 +46,26 @@ public class TopBarOptions implements DEFAULT_VALUES {
59 46
     @Nullable public ArrayList<Button> rightButtons;
60 47
 
61 48
     void mergeWith(final TopBarOptions other) {
62
-        if (other.testId.hasValue()) {
63
-            testId = other.testId;
64
-        }
65
-        if (other.title.hasValue())
66
-            title = other.title;
67
-        if (other.backgroundColor.hasValue())
68
-            backgroundColor = other.backgroundColor;
69
-        if (other.textColor.hasValue())
70
-            textColor = other.textColor;
71
-        if (other.textFontSize.hasValue())
72
-            textFontSize = other.textFontSize;
73
-        if (other.textFontFamily != null)
74
-            textFontFamily = other.textFontFamily;
75
-        if (other.visible.hasValue()) {
76
-            visible = other.visible;
77
-        }
78
-        if (other.animate.hasValue()) {
79
-            animate = other.animate;
80
-        }
81
-        if (other.hideOnScroll.hasValue()) {
82
-            hideOnScroll = other.hideOnScroll;
83
-        }
84
-        if (other.drawBehind.hasValue()) {
85
-            drawBehind = other.drawBehind;
86
-        }
87
-        if (other.leftButtons != null)
88
-            leftButtons = other.leftButtons;
89
-        if (other.rightButtons != null)
90
-            rightButtons = other.rightButtons;
49
+        title.mergeWith(other.title);
50
+        background.mergeWith(other.background);
51
+        if (other.testId.hasValue()) testId = other.testId;
52
+        if (other.visible.hasValue()) visible = other.visible;
53
+        if (other.animate.hasValue()) animate = other.animate;
54
+        if (other.hideOnScroll.hasValue()) hideOnScroll = other.hideOnScroll;
55
+        if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
56
+        if (other.leftButtons != null) leftButtons = other.leftButtons;
57
+        if (other.rightButtons != null) rightButtons = other.rightButtons;
91 58
     }
92 59
 
93 60
     void mergeWithDefault(TopBarOptions defaultOptions) {
94
-        if (title == null)
95
-            title = defaultOptions.title;
96
-        if (!backgroundColor.hasValue())
97
-            backgroundColor = defaultOptions.backgroundColor;
98
-        if (!textColor.hasValue())
99
-            textColor = defaultOptions.textColor;
100
-        if (!textFontSize.hasValue())
101
-            textFontSize = defaultOptions.textFontSize;
102
-        if (textFontFamily == null)
103
-            textFontFamily = defaultOptions.textFontFamily;
104
-        if (!visible.hasValue())
105
-            visible = defaultOptions.visible;
106
-        if (!animate.hasValue())
107
-            animate = defaultOptions.animate;
108
-        if (!hideOnScroll.hasValue())
109
-            hideOnScroll = defaultOptions.hideOnScroll;
110
-        if (!drawBehind.hasValue())
111
-            drawBehind = defaultOptions.drawBehind;
112
-        if (leftButtons == null)
113
-            leftButtons = defaultOptions.leftButtons;
114
-        if (rightButtons == null)
115
-            rightButtons = defaultOptions.rightButtons;
116
-        if (!testId.hasValue()) {
117
-            testId = defaultOptions.testId;
118
-        }
61
+        title.mergeWithDefault(defaultOptions.title);
62
+        background.mergeWithDefault(defaultOptions.background);
63
+        if (!visible.hasValue()) visible = defaultOptions.visible;
64
+        if (!animate.hasValue()) animate = defaultOptions.animate;
65
+        if (!hideOnScroll.hasValue()) hideOnScroll = defaultOptions.hideOnScroll;
66
+        if (!drawBehind.hasValue()) drawBehind = defaultOptions.drawBehind;
67
+        if (leftButtons == null) leftButtons = defaultOptions.leftButtons;
68
+        if (rightButtons == null) rightButtons = defaultOptions.rightButtons;
69
+        if (!testId.hasValue()) testId = defaultOptions.testId;
119 70
     }
120 71
 }

+ 9
- 15
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

@@ -16,21 +16,15 @@ import java.util.ArrayList;
16 16
 
17 17
 public class OptionsPresenter {
18 18
     private TopBar topBar;
19
-    private Component component;
20
-
21
-    public OptionsPresenter(TopBar topBar, Component component) {
22
-        this.topBar = topBar;
23
-        this.component = component;
24
-    }
25 19
 
26 20
     public OptionsPresenter(TopBar topBar) {
27 21
         this.topBar = topBar;
28 22
     }
29 23
 
30
-    public void applyOptions(Options options) {
24
+    public void applyChildOptions(Options options, Component child) {
31 25
         applyOrientation(options.orientationOptions);
32 26
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
33
-        applyTopBarOptions(options.topBarOptions);
27
+        applyTopBarOptions(options.topBarOptions, child);
34 28
         applyTopTabsOptions(options.topTabsOptions);
35 29
         applyTopTabOptions(options.topTabOptions);
36 30
     }
@@ -39,14 +33,15 @@ public class OptionsPresenter {
39 33
         ((Activity) topBar.getContext()).setRequestedOrientation(options.getValue());
40 34
     }
41 35
 
42
-    private void applyTopBarOptions(TopBarOptions options) {
43
-        if (options.title.hasValue()) topBar.setTitle(options.title.get());
44
-        topBar.setBackgroundColor(options.backgroundColor);
45
-        topBar.setTitleTextColor(options.textColor);
46
-        topBar.setTitleFontSize(options.textFontSize);
36
+    private void applyTopBarOptions(TopBarOptions options, Component component) {
37
+        if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
38
+        if (options.title.component.hasValue()) topBar.setComponent(options.title.component.get(), options.title.alignment);
39
+        topBar.setBackgroundColor(options.background.color);
40
+        topBar.setTitleTextColor(options.title.color);
41
+        topBar.setTitleFontSize(options.title.fontSize);
47 42
         if (options.testId.hasValue()) topBar.setTestId(options.testId.get());
48 43
 
49
-        topBar.setTitleTypeface(options.textFontFamily);
44
+        topBar.setTitleTypeface(options.title.fontFamily);
50 45
         if (options.visible.isFalse()) {
51 46
             topBar.hide(options.animate);
52 47
         }
@@ -58,7 +53,6 @@ public class OptionsPresenter {
58 53
         } else if (options.drawBehind.isFalseOrUndefined()) {
59 54
             component.drawBelowTopBar(topBar);
60 55
         }
61
-
62 56
         if (options.hideOnScroll.isTrue()) {
63 57
             if (component instanceof IReactView) {
64 58
                 topBar.enableCollapse(((IReactView) component).getScrollEventListener());

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ComponentViewController.java View File

@@ -62,7 +62,7 @@ public class ComponentViewController extends ViewController<ComponentLayout> imp
62 62
     public void mergeOptions(Options options) {
63 63
         this.options = this.options.mergeWith(options);
64 64
         view.applyOptions(this.options);
65
-        applyOnParentController(parentController -> parentController.applyOptions(this.options, view));
65
+        applyOnParentController(parentController -> parentController.applyChildOptions(this.options, view));
66 66
     }
67 67
 
68 68
     ReactComponent getComponent() {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ParentController.java View File

@@ -57,7 +57,7 @@ public abstract class ParentController<T extends ViewGroup> extends ViewControll
57 57
     }
58 58
 
59 59
     @CallSuper
60
-    public void applyOptions(Options options, Component childComponent) {
60
+    public void applyChildOptions(Options options, Component child) {
61 61
         mergeChildOptions(options);
62 62
     }
63 63
 

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/SideMenuController.java View File

@@ -49,10 +49,10 @@ public class SideMenuController extends ParentController<DrawerLayout> implement
49 49
 	}
50 50
 
51 51
     @Override
52
-    public void applyOptions(Options options, Component childComponent) {
53
-        super.applyOptions(options, childComponent);
52
+    public void applyChildOptions(Options options, Component child) {
53
+        super.applyChildOptions(options, child);
54 54
         applyOnParentController(parentController ->
55
-                ((ParentController) parentController).applyOptions(this.options, childComponent)
55
+                ((ParentController) parentController).applyChildOptions(this.options, child)
56 56
         );
57 57
     }
58 58
 

+ 13
- 10
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java View File

@@ -14,6 +14,7 @@ import com.reactnativenavigation.views.Component;
14 14
 import com.reactnativenavigation.views.ReactComponent;
15 15
 import com.reactnativenavigation.views.StackLayout;
16 16
 import com.reactnativenavigation.views.TopBar;
17
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
17 18
 
18 19
 import java.util.Collection;
19 20
 import java.util.Iterator;
@@ -25,28 +26,30 @@ public class StackController extends ParentController <StackLayout> {
25 26
     private static final NoOpPromise NO_OP = new NoOpPromise();
26 27
     private final IdStack<ViewController> stack = new IdStack<>();
27 28
     private final NavigationAnimator animator;
28
-    private ReactViewCreator topBarButtonCreator;
29
+    private final ReactViewCreator topBarButtonCreator;
30
+    private final TitleBarReactViewCreator titleBarReactViewCreator;
29 31
 
30
-    public StackController(final Activity activity, ReactViewCreator topBarButtonCreator, String id, Options initialOptions) {
32
+    public StackController(final Activity activity, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, String id, Options initialOptions) {
31 33
         super(activity, id, initialOptions);
32 34
         animator = new NavigationAnimator(activity);
33 35
         this.topBarButtonCreator = topBarButtonCreator;
36
+        this.titleBarReactViewCreator = titleBarReactViewCreator;
34 37
     }
35 38
 
36 39
     public void applyOptions(Options options) {
37 40
         super.applyOptions(options);
38
-        getView().applyOptions(options);
41
+        getView().applyChildOptions(options);
39 42
     }
40 43
 
41 44
     @Override
42
-    public void applyOptions(Options options, Component component) {
43
-        super.applyOptions(options, component);
44
-        getView().applyOptions(this.options, component);
45
+    public void applyChildOptions(Options options, Component child) {
46
+        super.applyChildOptions(options, child);
47
+        getView().applyChildOptions(this.options, child);
45 48
         applyOnParentController(parentController ->
46
-                ((ParentController) parentController).applyOptions(this.options.copy().clearTopBarOptions(), component)
49
+                ((ParentController) parentController).applyChildOptions(this.options.copy().clearTopBarOptions(), child)
47 50
         );
48
-        if (component instanceof ReactComponent) {
49
-            fabOptionsPresenter.applyOptions(options.fabOptions, (ReactComponent) component, getView());
51
+        if (child instanceof ReactComponent) {
52
+            fabOptionsPresenter.applyOptions(options.fabOptions, (ReactComponent) child, getView());
50 53
         }
51 54
         animator.setOptions(options.animationsOptions);
52 55
     }
@@ -202,7 +205,7 @@ public class StackController extends ParentController <StackLayout> {
202 205
     @NonNull
203 206
     @Override
204 207
     protected StackLayout createView() {
205
-        return new StackLayout(getActivity(), topBarButtonCreator, this::sendOnNavigationButtonPressed);
208
+        return new StackLayout(getActivity(), topBarButtonCreator, titleBarReactViewCreator, this::sendOnNavigationButtonPressed);
206 209
     }
207 210
 
208 211
 	@NonNull

+ 38
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java View File

@@ -0,0 +1,38 @@
1
+package com.reactnativenavigation.viewcontrollers;
2
+
3
+import android.app.Activity;
4
+
5
+import com.reactnativenavigation.parse.Options;
6
+import com.reactnativenavigation.utils.CompatUtils;
7
+import com.reactnativenavigation.views.titlebar.TitleBarReactView;
8
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
9
+
10
+public class TitleBarReactViewController extends ViewController<TitleBarReactView> {
11
+
12
+    private final TitleBarReactViewCreator reactViewCreator;
13
+    private String componentName;
14
+
15
+    public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator) {
16
+        super(activity, CompatUtils.generateViewId() + "", new Options());
17
+        this.reactViewCreator = reactViewCreator;
18
+    }
19
+
20
+    public TitleBarReactViewController(TitleBarReactViewController reactViewController) {
21
+        super(reactViewController.getActivity(), CompatUtils.generateViewId() + "", new Options());
22
+        this.reactViewCreator = reactViewController.reactViewCreator;
23
+    }
24
+
25
+    @Override
26
+    protected TitleBarReactView createView() {
27
+        return reactViewCreator.create(getActivity(), getId(), componentName);
28
+    }
29
+
30
+    @Override
31
+    public void sendOnNavigationButtonPressed(String buttonId) {
32
+
33
+    }
34
+
35
+    public void setComponent(String componentName) {
36
+        this.componentName = componentName;
37
+    }
38
+}

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java View File

@@ -18,7 +18,7 @@ import android.widget.TextView;
18 18
 import com.reactnativenavigation.parse.Options;
19 19
 import com.reactnativenavigation.parse.params.Button;
20 20
 import com.reactnativenavigation.parse.params.Text;
21
-import com.reactnativenavigation.react.TopBarReactButtonView;
21
+import com.reactnativenavigation.views.TopBarReactButtonView;
22 22
 import com.reactnativenavigation.utils.ArrayUtils;
23 23
 import com.reactnativenavigation.utils.ImageLoader;
24 24
 import com.reactnativenavigation.utils.ImageLoadingListenerAdapter;

+ 6
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/ViewController.java View File

@@ -15,6 +15,7 @@ import com.reactnativenavigation.presentation.FabOptionsPresenter;
15 15
 import com.reactnativenavigation.utils.CompatUtils;
16 16
 import com.reactnativenavigation.utils.StringUtils;
17 17
 import com.reactnativenavigation.utils.Task;
18
+import com.reactnativenavigation.utils.UiUtils;
18 19
 import com.reactnativenavigation.views.Component;
19 20
 
20 21
 public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener {
@@ -155,7 +156,7 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
155 156
         applyOptions(options);
156 157
         applyOnParentController(parentController -> {
157 158
             parentController.clearOptions();
158
-            if (getView() instanceof Component) parentController.applyOptions(options, (Component) getView());
159
+            if (getView() instanceof Component) parentController.applyChildOptions(options, (Component) getView());
159 160
         });
160 161
     }
161 162
 
@@ -201,6 +202,10 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
201 202
         }
202 203
     }
203 204
 
205
+    public void runOnPreDraw(Task<T> task) {
206
+        UiUtils.runOnPreDrawOnce(getView(), () -> task.run(getView()));
207
+    }
208
+
204 209
     public abstract void sendOnNavigationButtonPressed(String buttonId);
205 210
 
206 211
     protected boolean isViewShown() {

+ 4
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java View File

@@ -57,12 +57,12 @@ public class BottomTabsController extends ParentController implements AHBottomNa
57 57
     }
58 58
 
59 59
     @Override
60
-    public void applyOptions(Options options, Component childComponent) {
61
-        super.applyOptions(options, childComponent);
62
-        int tabIndex = bottomTabFinder.findByComponent(childComponent);
60
+    public void applyChildOptions(Options options, Component child) {
61
+        super.applyChildOptions(options, child);
62
+        int tabIndex = bottomTabFinder.findByComponent(child);
63 63
         if (tabIndex >= 0) new BottomTabsOptionsPresenter(bottomTabs, bottomTabFinder).present(this.options, tabIndex);
64 64
         applyOnParentController(parentController ->
65
-                ((ParentController) parentController).applyOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), childComponent)
65
+                ((ParentController) parentController).applyChildOptions(this.options.copy().clearBottomTabsOptions().clearBottomTabOptions(), child)
66 66
         );
67 67
     }
68 68
 

+ 3
- 3
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

@@ -79,11 +79,11 @@ public class TopTabsController extends ParentController<TopTabsViewPager> implem
79 79
     }
80 80
 
81 81
     @Override
82
-    public void applyOptions(Options options, Component childComponent) {
83
-        super.applyOptions(options, childComponent);
82
+    public void applyChildOptions(Options options, Component child) {
83
+        super.applyChildOptions(options, child);
84 84
         applyOnParentController(parentController -> {
85 85
                 Options opt = this.options.copy();
86
-                ((ParentController) parentController).applyOptions(opt.clearTopTabOptions().clearTopTabsOptions(), childComponent);
86
+                ((ParentController) parentController).applyChildOptions(opt.clearTopTabOptions().clearTopTabsOptions(), child);
87 87
             }
88 88
         );
89 89
     }

+ 14
- 12
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java View File

@@ -11,6 +11,7 @@ import com.reactnativenavigation.presentation.OptionsPresenter;
11 11
 import com.reactnativenavigation.utils.CompatUtils;
12 12
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
13 13
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
14
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
14 15
 
15 16
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
16 17
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -18,21 +19,27 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
18 19
 @SuppressLint("ViewConstructor")
19 20
 public class StackLayout extends RelativeLayout {
20 21
     private TopBar topBar;
22
+    private final OptionsPresenter optionsPresenter;
21 23
 
22
-    public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TopBarButtonController.OnClickListener topBarButtonClickListener) {
24
+    public StackLayout(Context context, ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarButtonController.OnClickListener topBarButtonClickListener) {
23 25
         super(context);
24
-        topBar = new TopBar(context, topBarButtonCreator, topBarButtonClickListener, this);
26
+        createLayout(topBarButtonCreator, titleBarReactViewCreator, topBarButtonClickListener);
27
+        optionsPresenter = new OptionsPresenter(topBar);
28
+        setContentDescription("StackLayout");
29
+    }
30
+
31
+    private void createLayout(ReactViewCreator topBarButtonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarButtonController.OnClickListener topBarButtonClickListener) {
32
+        topBar = new TopBar(getContext(), topBarButtonCreator, titleBarReactViewCreator, topBarButtonClickListener, this);
25 33
         topBar.setId(CompatUtils.generateViewId());
26 34
         addView(topBar, MATCH_PARENT, WRAP_CONTENT);
27
-        setContentDescription("StackLayout");
28 35
     }
29 36
 
30
-    public void applyOptions(Options options) {
31
-        new OptionsPresenter(topBar).applyOrientation(options.orientationOptions);
37
+    public void applyChildOptions(Options options) {
38
+        optionsPresenter.applyOrientation(options.orientationOptions);
32 39
     }
33 40
 
34
-    public void applyOptions(Options options, Component component) {
35
-        new OptionsPresenter(topBar, component).applyOptions(options);
41
+    public void applyChildOptions(Options options, Component child) {
42
+        optionsPresenter.applyChildOptions(options, child);
36 43
     }
37 44
 
38 45
     public void onChildWillDisappear(Options disappearing, Options appearing) {
@@ -55,9 +62,4 @@ public class StackLayout extends RelativeLayout {
55 62
     public TopBar getTopBar() {
56 63
         return topBar;
57 64
     }
58
-
59
-    @RestrictTo(RestrictTo.Scope.TESTS)
60
-    public void setTopBar(TopBar topBar) {
61
-        this.topBar = topBar;
62
-    }
63 65
 }

+ 17
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation.views;
2 2
 
3 3
 import android.annotation.SuppressLint;
4
+import android.app.Activity;
4 5
 import android.content.Context;
5 6
 import android.graphics.Typeface;
6 7
 import android.support.annotation.RestrictTo;
@@ -14,13 +15,17 @@ import android.widget.TextView;
14 15
 import com.reactnativenavigation.anim.TopBarAnimator;
15 16
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
16 17
 import com.reactnativenavigation.interfaces.ScrollEventListener;
18
+import com.reactnativenavigation.parse.TitleOptions;
17 19
 import com.reactnativenavigation.parse.params.Bool;
18 20
 import com.reactnativenavigation.parse.params.Button;
19 21
 import com.reactnativenavigation.parse.params.Color;
20 22
 import com.reactnativenavigation.parse.params.Fraction;
21 23
 import com.reactnativenavigation.parse.params.Number;
22 24
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
25
+import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
23 26
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
27
+import com.reactnativenavigation.views.titlebar.TitleBar;
28
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
24 29
 
25 30
 import java.util.List;
26 31
 
@@ -35,19 +40,23 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
35 40
     private TopTabs topTabs;
36 41
     private StackLayout parentView;
37 42
 
38
-    public TopBar(final Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView) {
43
+    public TopBar(final Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarButtonController.OnClickListener onClickListener, StackLayout parentView) {
39 44
         super(context);
40 45
         collapsingBehavior = new TopBarCollapseBehavior(this);
41 46
         topTabs = new TopTabs(getContext());
42 47
         animator = new TopBarAnimator(this);
43 48
         this.parentView = parentView;
44
-        titleBar = createTitleBar(context, buttonCreator, onClickListener);
49
+        titleBar = createTitleBar(context, buttonCreator, titleBarReactViewCreator, onClickListener);
45 50
         addView(titleBar);
46 51
         setContentDescription("TopBar");
47 52
     }
48 53
 
49
-    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener) {
50
-        return new TitleBar(context, buttonCreator, onClickListener);
54
+    protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
55
+        return new TitleBar(context,
56
+                buttonCreator,
57
+                new TitleBarReactViewController((Activity) context, reactViewCreator),
58
+                onClickListener
59
+        );
51 60
     }
52 61
 
53 62
     public void setTitle(String title) {
@@ -74,6 +83,10 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
74 83
         titleBar.setTitleTypeface(typeface);
75 84
     }
76 85
 
86
+    public void setComponent(String componentName, TitleOptions.Alignment alignment) {
87
+        titleBar.setComponent(componentName, alignment);
88
+    }
89
+
77 90
     public void setTopTabFontFamily(int tabIndex, Typeface fontFamily) {
78 91
         topTabs.setFontFamily(tabIndex, fontFamily);
79 92
     }

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

@@ -3,7 +3,6 @@ package com.reactnativenavigation.views;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
-import com.reactnativenavigation.react.TopBarReactButtonView;
7 6
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8 7
 
9 8
 public class TopBarButtonCreator implements ReactViewCreator {

lib/android/app/src/main/java/com/reactnativenavigation/react/TopBarReactButtonView.java → lib/android/app/src/main/java/com/reactnativenavigation/views/TopBarReactButtonView.java View File

@@ -1,9 +1,10 @@
1
-package com.reactnativenavigation.react;
1
+package com.reactnativenavigation.views;
2 2
 
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 
6 6
 import com.facebook.react.ReactInstanceManager;
7
+import com.reactnativenavigation.react.ReactView;
7 8
 
8 9
 @SuppressLint("ViewConstructor")
9 10
 public class TopBarReactButtonView extends ReactView {

lib/android/app/src/main/java/com/reactnativenavigation/views/TitleBar.java → lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.views;
1
+package com.reactnativenavigation.views.titlebar;
2 2
 
3 3
 import android.annotation.SuppressLint;
4 4
 import android.app.Activity;
@@ -7,29 +7,37 @@ import android.graphics.Typeface;
7 7
 import android.support.annotation.Nullable;
8 8
 import android.support.v7.widget.Toolbar;
9 9
 import android.util.Log;
10
+import android.view.Gravity;
10 11
 import android.view.View;
11 12
 import android.view.ViewGroup;
12 13
 import android.widget.TextView;
13 14
 
15
+import com.reactnativenavigation.parse.TitleOptions;
14 16
 import com.reactnativenavigation.parse.params.Button;
15 17
 import com.reactnativenavigation.parse.params.Color;
16 18
 import com.reactnativenavigation.parse.params.Fraction;
17 19
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
20
+import com.reactnativenavigation.viewcontrollers.TitleBarReactViewController;
18 21
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
19 22
 
20 23
 import java.util.ArrayList;
21 24
 import java.util.List;
22 25
 
26
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
27
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
28
+
23 29
 @SuppressLint("ViewConstructor")
24 30
 public class TitleBar extends Toolbar {
25 31
     private final ReactViewCreator buttonCreator;
32
+    private TitleBarReactViewController reactViewController;
26 33
     private final TopBarButtonController.OnClickListener onClickListener;
27 34
     private final List<TopBarButtonController> rightButtonControllers = new ArrayList<>();
28 35
     private TopBarButtonController leftButtonController;
29 36
 
30
-    public TitleBar(Context context, ReactViewCreator buttonCreator, TopBarButtonController.OnClickListener onClickListener) {
37
+    public TitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewController reactViewController, TopBarButtonController.OnClickListener onClickListener) {
31 38
         super(context);
32 39
         this.buttonCreator = buttonCreator;
40
+        this.reactViewController = reactViewController;
33 41
         this.onClickListener = onClickListener;
34 42
         getMenu();
35 43
         setContentDescription("titleBar");
@@ -39,29 +47,34 @@ public class TitleBar extends Toolbar {
39 47
         return super.getTitle() == null ? "" : (String) super.getTitle();
40 48
     }
41 49
 
42
-    void setTitleTextColor(Color color) {
50
+    public void setTitleTextColor(Color color) {
43 51
         if (color.hasValue()) setTitleTextColor(color.get());
44 52
     }
45 53
 
46
-    void setBackgroundColor(Color color) {
54
+    public void setComponent(String componentName, TitleOptions.Alignment alignment) {
55
+        reactViewController.setComponent(componentName);
56
+        addView(reactViewController.getView(), getComponentLayoutParams(alignment));
57
+    }
58
+
59
+    public void setBackgroundColor(Color color) {
47 60
         if (color.hasValue()) setBackgroundColor(color.get());
48 61
     }
49 62
 
50
-    void setTitleFontSize(Fraction size) {
63
+    public void setTitleFontSize(Fraction size) {
51 64
         TextView titleTextView = getTitleTextView();
52 65
         if (titleTextView != null && size.hasValue()) {
53 66
             titleTextView.setTextSize(size.get());
54 67
         }
55 68
     }
56 69
 
57
-    void setTitleTypeface(Typeface typeface) {
70
+    public void setTitleTypeface(Typeface typeface) {
58 71
         TextView titleTextView = getTitleTextView();
59 72
         if (titleTextView != null) {
60 73
             titleTextView.setTypeface(typeface);
61 74
         }
62 75
     }
63 76
 
64
-    TextView getTitleTextView() {
77
+    public TextView getTitleTextView() {
65 78
         return findTextView(this);
66 79
     }
67 80
 
@@ -69,6 +82,12 @@ public class TitleBar extends Toolbar {
69 82
         setTitle(null);
70 83
         clearRightButtons();
71 84
         clearLeftButton();
85
+        clearComponent();
86
+    }
87
+
88
+    private void clearComponent() {
89
+        reactViewController.destroy();
90
+        reactViewController = new TitleBarReactViewController(reactViewController);
72 91
     }
73 92
 
74 93
     private void clearLeftButton() {
@@ -137,4 +156,14 @@ public class TitleBar extends Toolbar {
137 156
         }
138 157
         return null;
139 158
     }
159
+
160
+    public Toolbar.LayoutParams getComponentLayoutParams(TitleOptions.Alignment alignment) {
161
+        if (alignment == TitleOptions.Alignment.Fill) {
162
+            return new LayoutParams(MATCH_PARENT, getHeight());
163
+        } else {
164
+            LayoutParams lp = new LayoutParams(WRAP_CONTENT, getHeight());
165
+            lp.gravity = Gravity.CENTER;
166
+            return lp;
167
+        }
168
+    }
140 169
 }

+ 23
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBarReactView.java View File

@@ -0,0 +1,23 @@
1
+package com.reactnativenavigation.views.titlebar;
2
+
3
+import android.annotation.SuppressLint;
4
+import android.content.Context;
5
+
6
+import com.facebook.react.ReactInstanceManager;
7
+import com.reactnativenavigation.react.ReactView;
8
+
9
+@SuppressLint("ViewConstructor")
10
+public class TitleBarReactView extends ReactView {
11
+
12
+    public TitleBarReactView(Context context, ReactInstanceManager reactInstanceManager, String componentId, String componentName) {
13
+        super(context, reactInstanceManager, componentId, componentName);
14
+    }
15
+
16
+    @Override
17
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
18
+        super.onMeasure(
19
+                getChildCount() > 0 ? MeasureSpec.makeMeasureSpec(getChildAt(0).getWidth(), MeasureSpec.EXACTLY) : widthMeasureSpec,
20
+                heightMeasureSpec
21
+        );
22
+    }
23
+}

+ 20
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBarReactViewCreator.java View File

@@ -0,0 +1,20 @@
1
+package com.reactnativenavigation.views.titlebar;
2
+
3
+import android.app.Activity;
4
+
5
+import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
7
+
8
+public class TitleBarReactViewCreator implements ReactViewCreator {
9
+
10
+    protected ReactInstanceManager instanceManager;
11
+
12
+    public TitleBarReactViewCreator(ReactInstanceManager instanceManager) {
13
+        this.instanceManager = instanceManager;
14
+	}
15
+
16
+	@Override
17
+	public TitleBarReactView create(Activity activity, String componentId, String componentName) {
18
+        return new TitleBarReactView(activity, instanceManager, componentId, componentName);
19
+    }
20
+}

+ 20
- 0
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TitleBarReactViewCreatorMock.java View File

@@ -0,0 +1,20 @@
1
+package com.reactnativenavigation.mocks;
2
+
3
+import android.app.Activity;
4
+
5
+import com.facebook.react.ReactInstanceManager;
6
+import com.reactnativenavigation.views.titlebar.TitleBarReactView;
7
+import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
8
+
9
+import static org.mockito.Mockito.mock;
10
+
11
+public class TitleBarReactViewCreatorMock extends TitleBarReactViewCreator {
12
+    public TitleBarReactViewCreatorMock() {
13
+        super(mock(ReactInstanceManager.class));
14
+    }
15
+
16
+    @Override
17
+    public TitleBarReactView create(Activity activity, String componentId, String componentName) {
18
+        return new TitleBarReactView(activity, instanceManager, componentId, componentName);
19
+    }
20
+}

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopBarButtonCreatorMock.java View File

@@ -3,7 +3,7 @@ package com.reactnativenavigation.mocks;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
-import com.reactnativenavigation.react.TopBarReactButtonView;
6
+import com.reactnativenavigation.views.TopBarReactButtonView;
7 7
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8 8
 
9 9
 import static org.mockito.Mockito.mock;

+ 31
- 21
lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java View File

@@ -31,6 +31,7 @@ public class OptionsTest extends BaseTest {
31 31
     private static final int TOP_BAR_FONT_SIZE = 18;
32 32
     private static final String TOP_BAR_FONT_FAMILY = "HelveticaNeue-CondensedBold";
33 33
     private static final Typeface TOP_BAR_TYPEFACE = Typeface.create("HelveticaNeue-CondensedBold", Typeface.BOLD);
34
+    private static final String TITLE_ALIGNMENT = "center";
34 35
     private static final Bool TOP_BAR_VISIBLE = new Bool(true);
35 36
     private static final Bool TOP_BAR_DRAW_BEHIND = new Bool(true);
36 37
     private static final Bool TOP_BAR_HIDE_ON_SCROLL = new Bool(true);
@@ -63,11 +64,11 @@ public class OptionsTest extends BaseTest {
63 64
     }
64 65
 
65 66
     private void assertResult(Options result) {
66
-        assertThat(result.topBarOptions.title.get()).isEqualTo(TITLE);
67
-        assertThat(result.topBarOptions.backgroundColor.get()).isEqualTo(TOP_BAR_BACKGROUND_COLOR);
68
-        assertThat(result.topBarOptions.textColor.get()).isEqualTo(TOP_BAR_TEXT_COLOR);
69
-        assertThat(result.topBarOptions.textFontSize.get()).isEqualTo(TOP_BAR_FONT_SIZE);
70
-        assertThat(result.topBarOptions.textFontFamily).isEqualTo(TOP_BAR_TYPEFACE);
67
+        assertThat(result.topBarOptions.title.text.get()).isEqualTo(TITLE);
68
+        assertThat(result.topBarOptions.background.color.get()).isEqualTo(TOP_BAR_BACKGROUND_COLOR);
69
+        assertThat(result.topBarOptions.title.color.get()).isEqualTo(TOP_BAR_TEXT_COLOR);
70
+        assertThat(result.topBarOptions.title.fontSize.get()).isEqualTo(TOP_BAR_FONT_SIZE);
71
+        assertThat(result.topBarOptions.title.fontFamily).isEqualTo(TOP_BAR_TYPEFACE);
71 72
         assertThat(result.topBarOptions.visible.get()).isEqualTo(TOP_BAR_VISIBLE.get());
72 73
         assertThat(result.topBarOptions.drawBehind.get()).isEqualTo(TOP_BAR_DRAW_BEHIND.get());
73 74
         assertThat(result.topBarOptions.hideOnScroll.get()).isEqualTo(TOP_BAR_HIDE_ON_SCROLL.get());
@@ -83,6 +84,7 @@ public class OptionsTest extends BaseTest {
83 84
         assertThat(result.fabOptions.hideOnScroll.get()).isEqualTo(FAB_HIDE_ON_SCROLL);
84 85
         assertThat(result.fabOptions.alignVertically.get()).isEqualTo(FAB_ALIGN_VERTICALLY);
85 86
         assertThat(result.fabOptions.alignHorizontally.get()).isEqualTo(FAB_ALIGN_HORIZONTALLY);
87
+        assertThat(result.topBarOptions.title.alignment).isEqualTo(TitleOptions.Alignment.Center);
86 88
     }
87 89
 
88 90
     @NonNull
@@ -97,16 +99,27 @@ public class OptionsTest extends BaseTest {
97 99
     @NonNull
98 100
     private JSONObject createTopBar(boolean visible) throws JSONException {
99 101
         return new JSONObject()
100
-                .put("title", "the title")
101
-                .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
102
-                .put("textColor", TOP_BAR_TEXT_COLOR)
103
-                .put("textFontSize", TOP_BAR_FONT_SIZE)
104
-                .put("textFontFamily", TOP_BAR_FONT_FAMILY)
102
+                .put("title", createTitle())
103
+                .put("background", createBackground())
105 104
                 .put("visible", visible)
106 105
                 .put("drawBehind", TOP_BAR_DRAW_BEHIND.get())
107 106
                 .put("hideOnScroll", TOP_BAR_HIDE_ON_SCROLL.get());
108 107
     }
109 108
 
109
+    private JSONObject createBackground() throws JSONException {
110
+        return new JSONObject()
111
+                .put("color", TOP_BAR_BACKGROUND_COLOR);
112
+    }
113
+
114
+    private JSONObject createTitle() throws JSONException {
115
+        return new JSONObject()
116
+                .put("text", "the title")
117
+                .put("color", TOP_BAR_TEXT_COLOR)
118
+                .put("fontSize", TOP_BAR_FONT_SIZE)
119
+                .put("fontFamily", TOP_BAR_FONT_FAMILY)
120
+                .put("alignment", TITLE_ALIGNMENT);
121
+    }
122
+
110 123
     @NonNull
111 124
     private JSONObject createFab() throws JSONException {
112 125
         return new JSONObject()
@@ -136,11 +149,8 @@ public class OptionsTest extends BaseTest {
136 149
     @NonNull
137 150
     private JSONObject createOtherTopBar() throws JSONException {
138 151
         return new JSONObject()
139
-                .put("title", "the title")
140
-                .put("backgroundColor", TOP_BAR_BACKGROUND_COLOR)
141
-                .put("textColor", TOP_BAR_TEXT_COLOR)
142
-                .put("textFontSize", TOP_BAR_FONT_SIZE)
143
-                .put("textFontFamily", TOP_BAR_FONT_FAMILY)
152
+                .put("title", createTitle())
153
+                .put("background", createBackground())
144 154
                 .put("visible", TOP_BAR_VISIBLE);
145 155
     }
146 156
 
@@ -159,17 +169,17 @@ public class OptionsTest extends BaseTest {
159 169
         JSONObject json1 = new JSONObject();
160 170
         json1.put("topBar", createTopBar(true));
161 171
         Options options1 = Options.parse(mockLoader, json1);
162
-        options1.topBarOptions.title = new Text("some title");
172
+        options1.topBarOptions.title.text = new Text("some title");
163 173
 
164 174
         JSONObject json2 = new JSONObject();
165 175
         json2.put("topBar", createTopBar(false));
166 176
         Options options2 = Options.parse(mockLoader, json2);
167
-        options2.topBarOptions.title = new NullText();
177
+        options2.topBarOptions.title.text = new NullText();
168 178
 
169 179
         Options merged = options1.mergeWith(options2);
170 180
         assertThat(options1.topBarOptions.visible.get()).isTrue();
171 181
         assertThat(merged.topBarOptions.visible.get()).isFalse();
172
-        assertThat(merged.topBarOptions.title.get()).isEqualTo("some title");
182
+        assertThat(merged.topBarOptions.title.text.get()).isEqualTo("some title");
173 183
     }
174 184
 
175 185
     @Test
@@ -203,7 +213,7 @@ public class OptionsTest extends BaseTest {
203 213
     @Test
204 214
     public void defaultEmptyOptions() throws Exception {
205 215
         Options uut = new Options();
206
-        assertThat(uut.topBarOptions.title.get("")).isEmpty();
216
+        assertThat(uut.topBarOptions.title.text.get("")).isEmpty();
207 217
     }
208 218
 
209 219
     @Test
@@ -216,9 +226,9 @@ public class OptionsTest extends BaseTest {
216 226
     @Test
217 227
     public void clear_topBarOptions() throws Exception {
218 228
         Options uut = new Options();
219
-        uut.topBarOptions.title = new Text("some title");
229
+        uut.topBarOptions.title.text = new Text("some title");
220 230
         uut.clearTopBarOptions();
221
-        assertThat(uut.topBarOptions.title.hasValue()).isFalse();
231
+        assertThat(uut.topBarOptions.title.text.hasValue()).isFalse();
222 232
     }
223 233
 
224 234
     @Test

+ 8
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/BottomTabsControllerTest.java View File

@@ -8,6 +8,7 @@ import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
9 9
 import com.reactnativenavigation.mocks.MockPromise;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 13
 import com.reactnativenavigation.parse.Options;
13 14
 import com.reactnativenavigation.parse.params.Color;
@@ -100,7 +101,7 @@ public class BottomTabsControllerTest extends BaseTest {
100 101
     public void findControllerById_ReturnsSelfOrChildren() throws Exception {
101 102
         assertThat(uut.findControllerById("123")).isNull();
102 103
         assertThat(uut.findControllerById(uut.getId())).isEqualTo(uut);
103
-        StackController inner = new StackController(activity, new TopBarButtonCreatorMock(), "inner", tabOptions);
104
+        StackController inner = createStack("inner");
104 105
         inner.animatePush(child1, new MockPromise());
105 106
         assertThat(uut.findControllerById(child1.getId())).isNull();
106 107
         uut.setTabs(Collections.singletonList(inner));
@@ -131,14 +132,14 @@ public class BottomTabsControllerTest extends BaseTest {
131 132
         uut.setTabs(tabs);
132 133
         uut.ensureViewIsCreated();
133 134
 
134
-        StackController stack = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
135
+        StackController stack = spy(createStack("stack"));
135 136
         stack.ensureViewIsCreated();
136 137
         stack.push(uut, new MockPromise());
137 138
 
138 139
         child1.onViewAppeared();
139 140
         ArgumentCaptor<Options> optionsCaptor = ArgumentCaptor.forClass(Options.class);
140 141
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
141
-        verify(stack, times(1)).applyOptions(optionsCaptor.capture(), viewCaptor.capture());
142
+        verify(stack, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
142 143
         assertThat(viewCaptor.getValue()).isEqualTo(child1.getView());
143 144
         assertThat(optionsCaptor.getValue().bottomTabsOptions.tabColor.hasValue()).isFalse();
144 145
     }
@@ -169,4 +170,8 @@ public class BottomTabsControllerTest extends BaseTest {
169 170
     private List<ViewController> createTabs() {
170 171
         return Arrays.asList(child1, child2, child3, child4, child5);
171 172
     }
173
+
174
+    private StackController createStack(String id) {
175
+        return new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), id, tabOptions);
176
+    }
172 177
 }

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

@@ -5,6 +5,7 @@ import android.app.Activity;
5 5
 import com.reactnativenavigation.BaseTest;
6 6
 import com.reactnativenavigation.mocks.TestComponentLayout;
7 7
 import com.reactnativenavigation.mocks.TestReactView;
8
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
8 9
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9 10
 import com.reactnativenavigation.parse.Options;
10 11
 import com.reactnativenavigation.views.StackLayout;
@@ -26,7 +27,7 @@ public class ComponentViewControllerTest extends BaseTest {
26 27
         super.beforeEach();
27 28
         Activity activity = newActivity();
28 29
         view = spy(new TestComponentLayout(activity, new TestReactView(activity)));
29
-        ParentController<StackLayout> parentController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
30
+        ParentController<StackLayout> parentController = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stack", new Options());
30 31
         uut = new ComponentViewController(activity, "componentId1", "componentName", (activity1, componentId, componentName) -> view, new Options());
31 32
         uut.setParentController(parentController);
32 33
         parentController.ensureViewIsCreated();

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

@@ -8,6 +8,7 @@ import android.view.ViewGroup;
8 8
 import com.reactnativenavigation.BaseTest;
9 9
 import com.reactnativenavigation.mocks.MockPromise;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 13
 import com.reactnativenavigation.parse.FabOptions;
13 14
 import com.reactnativenavigation.parse.Options;
@@ -33,7 +34,7 @@ public class FloatingActionButtonTest extends BaseTest {
33 34
     public void beforeEach() {
34 35
         super.beforeEach();
35 36
         activity = newActivity();
36
-        stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stackController", new Options());
37
+        stackController = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stackController", new Options());
37 38
         Options options = getOptionsWithFab();
38 39
         childFab = new SimpleViewController(activity, "child1", options);
39 40
         childNoFab = new SimpleViewController(activity, "child2", new Options());

+ 7
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/NavigatorTest.java View File

@@ -8,6 +8,7 @@ import com.reactnativenavigation.mocks.ImageLoaderMock;
8 8
 import com.reactnativenavigation.mocks.MockPromise;
9 9
 import com.reactnativenavigation.mocks.SimpleComponentViewController;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 13
 import com.reactnativenavigation.parse.Options;
13 14
 import com.reactnativenavigation.parse.params.Text;
@@ -46,7 +47,7 @@ public class NavigatorTest extends BaseTest {
46 47
         imageLoaderMock = ImageLoaderMock.mock();
47 48
         activity = newActivity();
48 49
         uut = new Navigator(activity);
49
-        parentController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
50
+        parentController = spy(newStack());
50 51
         parentController.ensureViewIsCreated();
51 52
         child1 = new SimpleViewController(activity, "child1", tabOptions);
52 53
         child2 = new SimpleViewController(activity, "child2", tabOptions);
@@ -226,14 +227,14 @@ public class NavigatorTest extends BaseTest {
226 227
     public void setOptions_CallsApplyNavigationOptions() {
227 228
         ComponentViewController componentVc = new SimpleComponentViewController(activity, "theId", new Options());
228 229
         componentVc.setParentController(parentController);
229
-        assertThat(componentVc.options.topBarOptions.title.get("")).isEmpty();
230
+        assertThat(componentVc.options.topBarOptions.title.text.get("")).isEmpty();
230 231
         uut.setRoot(componentVc, new MockPromise());
231 232
 
232 233
         Options options = new Options();
233
-        options.topBarOptions.title = new Text("new title");
234
+        options.topBarOptions.title.text = new Text("new title");
234 235
 
235 236
         uut.setOptions("theId", options);
236
-        assertThat(componentVc.options.topBarOptions.title.get()).isEqualTo("new title");
237
+        assertThat(componentVc.options.topBarOptions.title.text.get()).isEqualTo("new title");
237 238
     }
238 239
 
239 240
     @Test
@@ -248,7 +249,7 @@ public class NavigatorTest extends BaseTest {
248 249
 
249 250
     @NonNull
250 251
     private StackController newStack() {
251
-        return new StackController(activity, new TopBarButtonCreatorMock(), "stack" + CompatUtils.generateViewId(), tabOptions);
252
+        return new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stack" + CompatUtils.generateViewId(), tabOptions);
252 253
     }
253 254
 
254 255
     @Test
@@ -325,7 +326,7 @@ public class NavigatorTest extends BaseTest {
325 326
 
326 327
     @Test
327 328
     public void pushedStackCanBePopped() throws Exception {
328
-        StackController parent = new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options());
329
+        StackController parent = newStack();
329 330
         parent.ensureViewIsCreated();
330 331
         uut.setRoot(parent, new MockPromise());
331 332
         parent.push(parentController, new MockPromise());

+ 16
- 15
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java View File

@@ -10,6 +10,7 @@ import com.reactnativenavigation.BaseTest;
10 10
 import com.reactnativenavigation.mocks.MockPromise;
11 11
 import com.reactnativenavigation.mocks.TestComponentLayout;
12 12
 import com.reactnativenavigation.mocks.TestReactView;
13
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
13 14
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
14 15
 import com.reactnativenavigation.parse.Options;
15 16
 import com.reactnativenavigation.parse.params.Bool;
@@ -45,7 +46,7 @@ public class OptionsApplyingTest extends BaseTest {
45 46
                 (activity1, componentId, componentName) -> view,
46 47
                 initialNavigationOptions
47 48
         );
48
-        stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
49
+        stackController = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stack", new Options());
49 50
         stackController.ensureViewIsCreated();
50 51
         uut.setParentController(stackController);
51 52
     }
@@ -61,8 +62,8 @@ public class OptionsApplyingTest extends BaseTest {
61 62
 
62 63
     @Test
63 64
     public void initialOptionsAppliedOnAppear() throws Exception {
64
-        uut.options.topBarOptions.title = new Text("the title");
65
-        StackController stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stackId", new Options());
65
+        uut.options.topBarOptions.title.text = new Text("the title");
66
+        StackController stackController = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stackId", new Options());
66 67
         stackController.animatePush(uut, new MockPromise() {});
67 68
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
68 69
 
@@ -73,11 +74,11 @@ public class OptionsApplyingTest extends BaseTest {
73 74
     @Test
74 75
     public void mergeNavigationOptionsUpdatesCurrentOptions() throws Exception {
75 76
         uut.ensureViewIsCreated();
76
-        assertThat(uut.options.topBarOptions.title.get("")).isEmpty();
77
+        assertThat(uut.options.topBarOptions.title.text.get("")).isEmpty();
77 78
         Options options = new Options();
78
-        options.topBarOptions.title = new Text("new title");
79
+        options.topBarOptions.title.text = new Text("new title");
79 80
         uut.mergeOptions(options);
80
-        assertThat(uut.options.topBarOptions.title.get()).isEqualTo("new title");
81
+        assertThat(uut.options.topBarOptions.title.text.get()).isEqualTo("new title");
81 82
     }
82 83
 
83 84
     @Test
@@ -87,7 +88,7 @@ public class OptionsApplyingTest extends BaseTest {
87 88
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
88 89
 
89 90
         Options opts = new Options();
90
-        opts.topBarOptions.title = new Text("the new title");
91
+        opts.topBarOptions.title.text = new Text("the new title");
91 92
         uut.mergeOptions(opts);
92 93
 
93 94
         assertThat(stackController.getTopBar().getTitle()).isEqualTo("the new title");
@@ -99,7 +100,7 @@ public class OptionsApplyingTest extends BaseTest {
99 100
         uut.onViewAppeared();
100 101
 
101 102
         Options opts = new Options();
102
-        opts.topBarOptions.backgroundColor = new com.reactnativenavigation.parse.params.Color(Color.RED);
103
+        opts.topBarOptions.background.color = new com.reactnativenavigation.parse.params.Color(Color.RED);
103 104
         uut.mergeOptions(opts);
104 105
 
105 106
         assertThat(((ColorDrawable) stackController.getTopBar().getTitleBar().getBackground()).getColor()).isEqualTo(Color.RED);
@@ -112,8 +113,8 @@ public class OptionsApplyingTest extends BaseTest {
112 113
             @Override
113 114
             public void resolve(@Nullable Object value) {
114 115
                 Options opts = new Options();
115
-                opts.topBarOptions.title = new Text("the title");
116
-                opts.topBarOptions.textColor = new com.reactnativenavigation.parse.params.Color(Color.RED);
116
+                opts.topBarOptions.title.text = new Text("the title");
117
+                opts.topBarOptions.title.color = new com.reactnativenavigation.parse.params.Color(Color.RED);
117 118
                 uut.mergeOptions(opts);
118 119
 
119 120
                 assertThat(stackController.getTopBar().getTitleTextView()).isNotEqualTo(null);
@@ -125,13 +126,13 @@ public class OptionsApplyingTest extends BaseTest {
125 126
     @Test
126 127
     public void appliesTopBarTextSize() throws Exception {
127 128
         assertThat(uut.initialOptions).isSameAs(initialNavigationOptions);
128
-        initialNavigationOptions.topBarOptions.title = new Text("the title");
129
+        initialNavigationOptions.topBarOptions.title.text = new Text("the title");
129 130
         uut.ensureViewIsCreated();
130 131
         uut.onViewAppeared();
131 132
 
132 133
         Options opts = new Options();
133
-        opts.topBarOptions.title = new Text("the title");
134
-        opts.topBarOptions.textFontSize = new Fraction(18);
134
+        opts.topBarOptions.title.text = new Text("the title");
135
+        opts.topBarOptions.title.fontSize = new Fraction(18);
135 136
         uut.mergeOptions(opts);
136 137
 
137 138
         assertThat(stackController.getTopBar().getTitleTextView()).isNotEqualTo(null);
@@ -141,7 +142,7 @@ public class OptionsApplyingTest extends BaseTest {
141 142
     @Test
142 143
     public void appliesTopBarVisible() throws Exception {
143 144
         assertThat(uut.initialOptions).isSameAs(initialNavigationOptions);
144
-        initialNavigationOptions.topBarOptions.title = new Text("the title");
145
+        initialNavigationOptions.topBarOptions.title.text = new Text("the title");
145 146
         uut.ensureViewIsCreated();
146 147
         uut.onViewAppeared();
147 148
         assertThat(stackController.getTopBar().getVisibility()).isNotEqualTo(View.GONE);
@@ -156,7 +157,7 @@ public class OptionsApplyingTest extends BaseTest {
156 157
 
157 158
     @Test
158 159
     public void appliesDrawUnder() throws Exception {
159
-        uut.options.topBarOptions.title = new Text("the title");
160
+        uut.options.topBarOptions.title.text = new Text("the title");
160 161
         uut.options.topBarOptions.drawBehind = new Bool(false);
161 162
         uut.ensureViewIsCreated();
162 163
         stackController.animatePush(uut, new MockPromise() {

+ 13
- 8
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ParentControllerTest.java View File

@@ -8,6 +8,7 @@ import android.widget.FrameLayout;
8 8
 import com.reactnativenavigation.BaseTest;
9 9
 import com.reactnativenavigation.mocks.MockPromise;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 13
 import com.reactnativenavigation.parse.Options;
13 14
 import com.reactnativenavigation.parse.params.Text;
@@ -38,7 +39,7 @@ public class ParentControllerTest extends BaseTest {
38 39
         activity = newActivity();
39 40
         children = new ArrayList<>();
40 41
         Options initialOptions = new Options();
41
-        initialOptions.topBarOptions.title = new Text(INITIAL_TITLE);
42
+        initialOptions.topBarOptions.title.text = new Text(INITIAL_TITLE);
42 43
         uut = spy(new ParentController(activity, "uut", initialOptions) {
43 44
 
44 45
             @NonNull
@@ -88,7 +89,7 @@ public class ParentControllerTest extends BaseTest {
88 89
 
89 90
     @Test
90 91
     public void findControllerById_Recursive() throws Exception {
91
-        StackController stackController = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
92
+        StackController stackController = createStack();
92 93
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
93 94
         SimpleViewController child2 = new SimpleViewController(activity, "child2", new Options());
94 95
         stackController.animatePush(child1, new MockPromise());
@@ -110,7 +111,7 @@ public class ParentControllerTest extends BaseTest {
110 111
 
111 112
     @Test
112 113
     public void optionsAreClearedWhenChildIsAppeared() throws Exception {
113
-        StackController stackController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
114
+        StackController stackController = spy(createStack());
114 115
         SimpleViewController child1 = new SimpleViewController(activity, "child1", new Options());
115 116
         stackController.animatePush(child1, new MockPromise());
116 117
 
@@ -121,7 +122,7 @@ public class ParentControllerTest extends BaseTest {
121 122
     @Test
122 123
     public void mergeOptions_optionsAreMergedWhenChildAppears() throws Exception {
123 124
         Options options = new Options();
124
-        options.topBarOptions.title = new Text("new title");
125
+        options.topBarOptions.title.text = new Text("new title");
125 126
         ViewController child1 = spy(new SimpleViewController(activity, "child1", options));
126 127
         children.add(child1);
127 128
         uut.ensureViewIsCreated();
@@ -131,15 +132,15 @@ public class ParentControllerTest extends BaseTest {
131 132
         ArgumentCaptor<Options> optionsCaptor = ArgumentCaptor.forClass(Options.class);
132 133
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
133 134
         verify(uut, times(1)).clearOptions();
134
-        verify(uut, times(1)).applyOptions(optionsCaptor.capture(), viewCaptor.capture());
135
-        assertThat(optionsCaptor.getValue().topBarOptions.title.get()).isEqualTo("new title");
135
+        verify(uut, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
136
+        assertThat(optionsCaptor.getValue().topBarOptions.title.text.get()).isEqualTo("new title");
136 137
         assertThat(viewCaptor.getValue()).isEqualTo(child1.getView());
137 138
     }
138 139
 
139 140
     @Test
140 141
     public void mergeOptions_initialParentOptionsAreNotMutatedWhenChildAppears() throws Exception {
141 142
         Options options = new Options();
142
-        options.topBarOptions.title = new Text("new title");
143
+        options.topBarOptions.title.text = new Text("new title");
143 144
         ViewController child1 = spy(new SimpleViewController(activity, "child1", options));
144 145
         children.add(child1);
145 146
 
@@ -147,6 +148,10 @@ public class ParentControllerTest extends BaseTest {
147 148
 
148 149
         child1.ensureViewIsCreated();
149 150
         child1.onViewAppeared();
150
-        assertThat(uut.initialOptions.topBarOptions.title.get()).isEqualTo(INITIAL_TITLE);
151
+        assertThat(uut.initialOptions.topBarOptions.title.text.get()).isEqualTo(INITIAL_TITLE);
152
+    }
153
+
154
+    private StackController createStack() {
155
+        return new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stack", new Options());
151 156
     }
152 157
 }

+ 15
- 11
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java View File

@@ -7,6 +7,7 @@ import android.view.View;
7 7
 import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.mocks.MockPromise;
9 9
 import com.reactnativenavigation.mocks.SimpleViewController;
10
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10 11
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11 12
 import com.reactnativenavigation.parse.Options;
12 13
 import com.reactnativenavigation.parse.params.Bool;
@@ -39,7 +40,7 @@ public class StackControllerTest extends BaseTest {
39 40
     public void beforeEach() {
40 41
         super.beforeEach();
41 42
         activity = newActivity();
42
-        uut = new StackController(activity, new TopBarButtonCreatorMock(), "uut", new Options());
43
+        uut = createStackController("uut");
43 44
         child1 = spy(new SimpleViewController(activity, "child1", new Options()));
44 45
         child2 = spy(new SimpleViewController(activity, "child2", new Options()));
45 46
         child3 = spy(new SimpleViewController(activity, "child3", new Options()));
@@ -87,7 +88,7 @@ public class StackControllerTest extends BaseTest {
87 88
             @Override
88 89
             public void resolve(@Nullable Object value) {
89 90
                 uut.pop(new MockPromise());
90
-                verify(uut, times(1)).applyOptions(uut.options, eq((ReactComponent) child1.getView()));
91
+                verify(uut, times(1)).applyChildOptions(uut.options, eq((ReactComponent) child1.getView()));
91 92
             }
92 93
         });
93 94
     }
@@ -95,7 +96,7 @@ public class StackControllerTest extends BaseTest {
95 96
     @Test
96 97
     public void pop_layoutHandlesChildWillDisappear() throws Exception {
97 98
         final StackLayout[] stackLayout = new StackLayout[1];
98
-        uut = new StackController(activity, new TopBarButtonCreatorMock(), "uut", new Options()) {
99
+        uut = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "uut", new Options()) {
99 100
             @NonNull
100 101
             @Override
101 102
             protected StackLayout createView() {
@@ -134,7 +135,7 @@ public class StackControllerTest extends BaseTest {
134 135
         uut.animatePush(child1, new MockPromise());
135 136
         assertThat(child1.getParentController()).isEqualTo(uut);
136 137
 
137
-        StackController anotherNavController = new StackController(activity, new TopBarButtonCreatorMock(), "another", new Options());
138
+        StackController anotherNavController = createStackController("another");
138 139
         anotherNavController.animatePush(child2, new MockPromise());
139 140
         assertThat(child2.getParentController()).isEqualTo(anotherNavController);
140 141
     }
@@ -314,7 +315,7 @@ public class StackControllerTest extends BaseTest {
314 315
 
315 316
     @Test
316 317
     public void findControllerById_Deeply() throws Exception {
317
-        StackController stack = new StackController(activity, new TopBarButtonCreatorMock(), "stack2", new Options());
318
+        StackController stack = createStackController("stack2");
318 319
         stack.animatePush(child2, new MockPromise());
319 320
         uut.animatePush(stack, new MockPromise());
320 321
         assertThat(uut.findControllerById(child2.getId())).isEqualTo(child2);
@@ -351,7 +352,6 @@ public class StackControllerTest extends BaseTest {
351 352
     @Test
352 353
     public void pop_animatesTopBarIfNeeded() throws Exception {
353 354
         uut.ensureViewIsCreated();
354
-        uut.getView().setTopBar(spy(uut.getTopBar()));
355 355
 
356 356
         child1.options.topBarOptions.visible = new Bool(false);
357 357
         child1.options.topBarOptions.animate = new Bool(false);
@@ -409,7 +409,7 @@ public class StackControllerTest extends BaseTest {
409 409
 
410 410
     @Test
411 411
     public void stackCanBePushed() throws Exception {
412
-        StackController parent = new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options());
412
+        StackController parent = createStackController("someStack");
413 413
         parent.ensureViewIsCreated();
414 414
         parent.push(uut, new MockPromise());
415 415
         uut.onViewAppeared();
@@ -418,12 +418,12 @@ public class StackControllerTest extends BaseTest {
418 418
 
419 419
     @Test
420 420
     public void applyOptions_applyOnlyOnFirstStack() throws Exception {
421
-        StackController parent = spy(new StackController(activity, new TopBarButtonCreatorMock(), "someStack", new Options()));
421
+        StackController parent = spy(createStackController("someStack"));
422 422
         parent.ensureViewIsCreated();
423 423
         parent.push(uut, new MockPromise());
424 424
 
425 425
         Options childOptions = new Options();
426
-        childOptions.topBarOptions.title = new Text("Something");
426
+        childOptions.topBarOptions.title.text = new Text("Something");
427 427
         child1.options = childOptions;
428 428
         uut.push(child1, new MockPromise());
429 429
         child1.ensureViewIsCreated();
@@ -431,8 +431,8 @@ public class StackControllerTest extends BaseTest {
431 431
 
432 432
         ArgumentCaptor<Options> optionsCaptor = ArgumentCaptor.forClass(Options.class);
433 433
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
434
-        verify(parent, times(1)).applyOptions(optionsCaptor.capture(), viewCaptor.capture());
435
-        assertThat(optionsCaptor.getValue().topBarOptions.title.hasValue()).isFalse();
434
+        verify(parent, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
435
+        assertThat(optionsCaptor.getValue().topBarOptions.title.text.hasValue()).isFalse();
436 436
     }
437 437
 
438 438
     @Test
@@ -456,4 +456,8 @@ public class StackControllerTest extends BaseTest {
456 456
         assertThat(uut.size()).isEqualTo(ids.length);
457 457
         assertThat(uut.getChildControllers()).extracting((Extractor<ViewController, String>) ViewController::getId).containsOnly(ids);
458 458
     }
459
+
460
+    private StackController createStackController(String id) {
461
+        return new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), id, new Options());
462
+    }
459 463
 }

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

@@ -1,15 +1,22 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
+import android.support.v7.widget.Toolbar;
5
+import android.view.Gravity;
6
+import android.view.ViewGroup;
4 7
 
5 8
 import com.reactnativenavigation.BaseTest;
9
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
6 10
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
+import com.reactnativenavigation.parse.TitleOptions;
7 12
 import com.reactnativenavigation.parse.params.Button;
8 13
 import com.reactnativenavigation.parse.params.Text;
9 14
 import com.reactnativenavigation.react.ReactView;
10
-import com.reactnativenavigation.views.TitleBar;
15
+import com.reactnativenavigation.views.titlebar.TitleBar;
16
+import com.reactnativenavigation.views.titlebar.TitleBarReactView;
11 17
 
12 18
 import org.junit.Test;
19
+import org.mockito.ArgumentCaptor;
13 20
 
14 21
 import java.util.ArrayList;
15 22
 import java.util.Arrays;
@@ -19,6 +26,7 @@ import java.util.List;
19 26
 import java.util.Map;
20 27
 
21 28
 import static org.assertj.core.api.Java6Assertions.assertThat;
29
+import static org.mockito.ArgumentMatchers.any;
22 30
 import static org.mockito.Mockito.spy;
23 31
 import static org.mockito.Mockito.times;
24 32
 import static org.mockito.Mockito.verify;
@@ -30,14 +38,16 @@ public class TitleBarTest extends BaseTest {
30 38
     private Button textButton;
31 39
     private Button customButton;
32 40
     private Map<String, TopBarButtonController> buttonControllers;
41
+    private TitleBarReactViewController reactViewController;
33 42
 
34 43
     @Override
35 44
     public void beforeEach() {
36 45
         final TopBarButtonCreatorMock buttonCreator = new TopBarButtonCreatorMock();
37 46
         final Activity activity = newActivity();
47
+        reactViewController = spy(new TitleBarReactViewController(activity, new TitleBarReactViewCreatorMock()));
38 48
         createButtons();
39 49
         buttonControllers = new HashMap<>();
40
-        uut = spy(new TitleBar(activity, buttonCreator, (buttonId -> {})) {
50
+        uut = spy(new TitleBar(activity, buttonCreator, reactViewController, (buttonId -> {})) {
41 51
             @Override
42 52
             public TopBarButtonController createButtonController(Button button) {
43 53
                 TopBarButtonController controller = spy(super.createButtonController(button));
@@ -120,6 +130,40 @@ public class TitleBarTest extends BaseTest {
120 130
         assertThat(uut.getMenu().getItem(1).getTitle()).isEqualTo(textButton.title.get());
121 131
     }
122 132
 
133
+    @Test
134
+    public void setComponent_addsComponentToTitleBar() throws Exception {
135
+        uut.setComponent("com.rnn.CustomView", TitleOptions.Alignment.Center);
136
+        verify(uut, times(1)).addView(any(TitleBarReactView.class), any(Toolbar.LayoutParams.class));
137
+    }
138
+
139
+    @Test
140
+    public void setComponent_alignFill() throws Exception {
141
+        uut.setComponent("com.rnn.CustomView", TitleOptions.Alignment.Fill);
142
+        verify(uut, times(1)).getComponentLayoutParams(TitleOptions.Alignment.Fill);
143
+        ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
144
+        verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
145
+        assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.MATCH_PARENT);
146
+    }
147
+
148
+    @Test
149
+    public void setComponent_alignCenter() throws Exception {
150
+        uut.setComponent("com.rnn.CustomView", TitleOptions.Alignment.Center);
151
+        verify(uut, times(1)).getComponentLayoutParams(TitleOptions.Alignment.Center);
152
+        ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
153
+        verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
154
+        assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.WRAP_CONTENT);
155
+        assertThat(lpCaptor.getValue().gravity == Gravity.CENTER);
156
+    }
157
+
158
+    @Test
159
+    public void clear() throws Exception {
160
+        uut.clear();
161
+        assertThat(uut.getTitle()).isNullOrEmpty();
162
+        assertThat(uut.getMenu().size()).isZero();
163
+        assertThat(uut.getNavigationIcon()).isNull();
164
+        verify(reactViewController, times(1)).destroy();
165
+    }
166
+
123 167
     private List<Button> leftButton(Button leftButton) {
124 168
         return Collections.singletonList(leftButton);
125 169
     }

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

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 import android.support.annotation.NonNull;
5 5
 
6 6
 import com.reactnativenavigation.BaseTest;
7
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
7 8
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
8 9
 import com.reactnativenavigation.parse.Options;
9 10
 import com.reactnativenavigation.parse.params.Button;
@@ -28,7 +29,7 @@ public class TopBarButtonControllerTest extends BaseTest {
28 29
 
29 30
         TopBarButtonCreatorMock buttonCreatorMock = new TopBarButtonCreatorMock();
30 31
         uut = spy(new TopBarButtonController(activity, button, buttonCreatorMock, (buttonId) -> {}));
31
-        stackController = spy(new StackController(activity, buttonCreatorMock, "stack", new Options()));
32
+        stackController = spy(new StackController(activity, buttonCreatorMock, new TitleBarReactViewCreatorMock(), "stack", new Options()));
32 33
 
33 34
     }
34 35
 

+ 17
- 11
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

@@ -8,6 +8,7 @@ import com.reactnativenavigation.BaseTest;
8 8
 import com.reactnativenavigation.mocks.MockPromise;
9 9
 import com.reactnativenavigation.mocks.TestComponentViewCreator;
10 10
 import com.reactnativenavigation.mocks.TestReactView;
11
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
11 12
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12 13
 import com.reactnativenavigation.parse.Options;
13 14
 import com.reactnativenavigation.parse.params.Text;
@@ -60,18 +61,23 @@ public class TopTabsViewControllerTest extends BaseTest {
60 61
         uut = spy(new TopTabsController(activity, "componentId", tabControllers, layoutCreator, options));
61 62
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
62 63
 
63
-        parentController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stackId", new Options()));
64
+        parentController = spy(createStackController("stackId"));
64 65
         parentController.push(uut, new MockPromise());
65 66
         uut.setParentController(parentController);
66 67
     }
67 68
 
69
+    @NonNull
70
+    private StackController createStackController(String id) {
71
+        return new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), id, new Options());
72
+    }
73
+
68 74
     @NonNull
69 75
     private ArrayList<Options> createOptions() {
70 76
         ArrayList result = new ArrayList();
71 77
         for (int i = 0; i < SIZE; i++) {
72 78
             final Options options = new Options();
73 79
             options.topTabOptions.title = new Text("Tab " + i);
74
-            options.topBarOptions.title = new Text(createTabTopBarTitle(i));
80
+            options.topBarOptions.title.text = new Text(createTabTopBarTitle(i));
75 81
             result.add(options);
76 82
         }
77 83
         return result;
@@ -160,7 +166,7 @@ public class TopTabsViewControllerTest extends BaseTest {
160 166
         verify(tabControllers.get(1), times(0)).onViewAppeared();
161 167
 
162 168
         ReactComponent comp = ((ComponentViewController) tabControllers.get(0)).getComponent();
163
-        verify(uut, times(1)).applyOptions(any(Options.class), eq(comp));
169
+        verify(uut, times(1)).applyChildOptions(any(Options.class), eq(comp));
164 170
     }
165 171
 
166 172
     @Test
@@ -172,18 +178,18 @@ public class TopTabsViewControllerTest extends BaseTest {
172 178
 
173 179
         uut.onViewAppeared();
174 180
         ReactComponent currentTab = tabView(0);
175
-        verify(uut, times(1)).applyOptions(any(Options.class), eq(currentTab));
176
-        assertThat(uut.options.topBarOptions.title.get()).isEqualTo(createTabTopBarTitle(0));
181
+        verify(uut, times(1)).applyChildOptions(any(Options.class), eq(currentTab));
182
+        assertThat(uut.options.topBarOptions.title.text.get()).isEqualTo(createTabTopBarTitle(0));
177 183
 
178 184
         uut.switchToTab(1);
179 185
         currentTab = tabView(1);
180
-        verify(uut, times(1)).applyOptions(any(Options.class), eq(currentTab));
181
-        assertThat(uut.options.topBarOptions.title.get()).isEqualTo(createTabTopBarTitle(1));
186
+        verify(uut, times(1)).applyChildOptions(any(Options.class), eq(currentTab));
187
+        assertThat(uut.options.topBarOptions.title.text.get()).isEqualTo(createTabTopBarTitle(1));
182 188
 
183 189
         uut.switchToTab(0);
184 190
         currentTab = tabView(0);
185
-        verify(uut, times(2)).applyOptions(any(Options.class), eq(currentTab));
186
-        assertThat(uut.options.topBarOptions.title.get()).isEqualTo(createTabTopBarTitle(0));
191
+        verify(uut, times(2)).applyChildOptions(any(Options.class), eq(currentTab));
192
+        assertThat(uut.options.topBarOptions.title.text.get()).isEqualTo(createTabTopBarTitle(0));
187 193
     }
188 194
 
189 195
     private TestReactView getActualTabView(int index) {
@@ -207,7 +213,7 @@ public class TopTabsViewControllerTest extends BaseTest {
207 213
         tabControllers.get(0).onViewAppeared();
208 214
         ArgumentCaptor<Options> optionsCaptor = ArgumentCaptor.forClass(Options.class);
209 215
         ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
210
-        verify(parentController, times(1)).applyOptions(optionsCaptor.capture(), viewCaptor.capture());
216
+        verify(parentController, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
211 217
         assertThat(optionsCaptor.getValue().topTabOptions.title.hasValue()).isFalse();
212 218
     }
213 219
 
@@ -215,7 +221,7 @@ public class TopTabsViewControllerTest extends BaseTest {
215 221
     public void applyOptions_tabsAreRemovedAfterViewDisappears() throws Exception {
216 222
         parentController.getView().removeAllViews();
217 223
 
218
-        StackController stackController = spy(new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options()));
224
+        StackController stackController = spy(createStackController("stack"));
219 225
         ComponentViewController first = new ComponentViewController(
220 226
                 activity,
221 227
                 "firstScreen",

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

@@ -9,6 +9,7 @@ import android.widget.LinearLayout;
9 9
 import com.reactnativenavigation.BaseTest;
10 10
 import com.reactnativenavigation.mocks.MockPromise;
11 11
 import com.reactnativenavigation.mocks.SimpleViewController;
12
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
12 13
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
13 14
 import com.reactnativenavigation.parse.Options;
14 15
 
@@ -66,7 +67,7 @@ public class ViewControllerTest extends BaseTest {
66 67
     @Test
67 68
     public void holdsAReferenceToStackControllerOrNull() throws Exception {
68 69
         assertThat(uut.getParentController()).isNull();
69
-        StackController nav = new StackController(activity, new TopBarButtonCreatorMock(), "stack", new Options());
70
+        StackController nav = new StackController(activity, new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), "stack", new Options());
70 71
         nav.animatePush(uut, new MockPromise());
71 72
         assertThat(uut.getParentController()).isEqualTo(nav);
72 73
     }

+ 3
- 2
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java View File

@@ -5,6 +5,7 @@ import android.view.MenuItem;
5 5
 
6 6
 import com.reactnativenavigation.BaseTest;
7 7
 import com.reactnativenavigation.anim.TopBarAnimator;
8
+import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
8 9
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9 10
 import com.reactnativenavigation.parse.params.Bool;
10 11
 import com.reactnativenavigation.parse.params.Button;
@@ -38,8 +39,8 @@ public class TopBarTest extends BaseTest {
38 39
                 Log.i("TopBarTest", "onPress: " + buttonId);
39 40
             }
40 41
         });
41
-        StackLayout parent = new StackLayout(newActivity(), new TopBarButtonCreatorMock(), this.onClickListener);
42
-        uut = new TopBar(newActivity(), new TopBarButtonCreatorMock(), this.onClickListener, parent);
42
+        StackLayout parent = new StackLayout(newActivity(), new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), this.onClickListener);
43
+        uut = new TopBar(newActivity(), new TopBarButtonCreatorMock(), new TitleBarReactViewCreatorMock(), this.onClickListener, parent);
43 44
         animator = spy(new TopBarAnimator(uut));
44 45
         uut.setAnimator(animator);
45 46
         leftButton = createLeftButton();

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

@@ -15,6 +15,8 @@ extern const NSInteger BLUR_TOPBAR_TAG;
15 15
 	
16 16
 	if (self.visible) {
17 17
 		[((RNNTabBarController *)viewController.tabBarController) setTabBarHidden:![self.visible boolValue] animated:[self.animate boolValue]];
18
+	} else {
19
+		[((RNNTabBarController *)viewController.tabBarController) setTabBarHidden:NO animated:NO];
18 20
 	}
19 21
 	
20 22
 	if (self.testID) {
@@ -31,10 +33,14 @@ extern const NSInteger BLUR_TOPBAR_TAG;
31 33
 	
32 34
 	if (self.backgroundColor) {
33 35
 		viewController.tabBarController.tabBar.barTintColor = [RCTConvert UIColor:self.backgroundColor];
36
+	} else {
37
+		viewController.tabBarController.tabBar.barTintColor = nil;
34 38
 	}
35 39
 	
36 40
 	if (self.translucent) {
37 41
 		viewController.tabBarController.tabBar.translucent = [self.translucent boolValue];
42
+	} else {
43
+		viewController.tabBarController.tabBar.translucent = NO;
38 44
 	}
39 45
 	
40 46
 	if (self.hideShadow) {

+ 5
- 2
lib/ios/RNNCustomTitleView.h View File

@@ -1,7 +1,10 @@
1 1
 #import <UIKit/UIKit.h>
2
+#import <React/RCTRootView.h>
3
+#import <React/RCTRootViewDelegate.h>
2 4
 
3
-@interface RNNCustomTitleView : UIView
5
+@interface RNNCustomTitleView : UIView <RCTRootViewDelegate>
4 6
 
5
--(instancetype)initWithFrame:(CGRect)frame subView:(UIView*)subView alignment:(NSString*)alignment;
7
+- (instancetype)initWithFrame:(CGRect)frame subView:(RCTRootView*)subView alignment:(NSString*)alignment;
6 8
 
7 9
 @end
10
+

+ 41
- 33
lib/ios/RNNCustomTitleView.m View File

@@ -1,47 +1,55 @@
1 1
 #import "RNNCustomTitleView.h"
2 2
 
3 3
 @interface RNNCustomTitleView ()
4
-@property (nonatomic, strong) UIView *subView;
5
-@property (nonatomic, strong) NSString *subViewAlign;
4
+
5
+@property (nonatomic, strong) RCTRootView *subView;
6
+@property (nonatomic, strong) NSString *alignment;
7
+
6 8
 @end
7 9
 
8 10
 @implementation RNNCustomTitleView
9 11
 
12
+- (instancetype)initWithFrame:(CGRect)frame subView:(RCTRootView*)subView alignment:(NSString*)alignment {
13
+	self = [super init];
14
+	
15
+	if (self) {
16
+		self.subView = subView;
17
+		self.alignment = alignment;
18
+		
19
+		self.backgroundColor = [UIColor clearColor];
20
+		self.subView.backgroundColor = [UIColor clearColor];
21
+		
22
+		if ([alignment isEqualToString:@"fill"]) {
23
+			self.frame = frame;
24
+			subView.sizeFlexibility = RCTRootViewSizeFlexibilityNone;
25
+		} else {
26
+			self.subView.delegate = self;
27
+			subView.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight;
28
+		}
29
+		
30
+		[self addSubview:subView];
31
+	}
32
+	
33
+	return self;
34
+}
10 35
 
11
--(instancetype)initWithFrame:(CGRect)frame subView:(UIView*)subView alignment:(NSString*)alignment {
12
-    self = [super initWithFrame:frame];
13
-    
14
-    if (self) {
15
-        self.backgroundColor = [UIColor clearColor];
16
-        self.subView = subView;
17
-        self.subViewAlign = alignment;
18
-        
19
-        subView.frame = self.bounds;
20
-        [self addSubview:subView];
21
-    }
22
-    
23
-    return self;
36
+- (void)layoutSubviews {
37
+	[super layoutSubviews];
38
+	if ([self.alignment isEqualToString:@"fill"]) {
39
+		[self.subView setFrame:self.bounds];
40
+	}
24 41
 }
25 42
 
43
+- (NSString *)alignment {
44
+	return _alignment ? _alignment : @"center";
45
+}
26 46
 
27
--(void)layoutSubviews {
28
-    [super layoutSubviews];
29
-    
30
-    if ([self.subViewAlign isEqualToString:@"fill"]) {
31
-        self.subView.frame = self.bounds;
32
-    }
33
-    else {
34
-        
35
-        CGFloat superViewWidth = self.superview.frame.size.width;
36
-        CGFloat paddingLeftFromCenter = (superViewWidth/2) - self.frame.origin.x;
37
-        CGFloat paddingRightFromCenter = self.frame.size.width - paddingLeftFromCenter;;
38
-        CGRect reactViewFrame = self.subView.bounds;
39
-        CGFloat minPadding = MIN(paddingLeftFromCenter, paddingRightFromCenter);
40
-        
41
-        reactViewFrame.size.width = minPadding*2;
42
-        reactViewFrame.origin.x = paddingLeftFromCenter - minPadding;
43
-        self.subView.frame = reactViewFrame;
44
-    }
47
+- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView {
48
+	if ([self.alignment isEqualToString:@"center"]) {
49
+		[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.subView.intrinsicContentSize.width, self.subView.intrinsicContentSize.height)];
50
+		[self.subView setFrame:CGRectMake(0, 0, rootView.intrinsicContentSize.width, rootView.intrinsicContentSize.height)];
51
+	}
45 52
 }
46 53
 
47 54
 @end
55
+

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

@@ -101,6 +101,12 @@
101 101
 		[barButtonItem setTintColor:[RCTConvert UIColor: tintColor]];
102 102
 	}
103 103
 	
104
+	NSNumber* disabledColor = dictionary[@"disabledColor"];
105
+	if (disabledColor) {
106
+		UIColor *color = [RCTConvert UIColor:disabledColor];
107
+		[barButtonItem setTitleTextAttributes:@{NSForegroundColorAttributeName : color} forState:UIControlStateDisabled];
108
+	}
109
+	
104 110
 	NSString *testID = dictionary[@"testID"];
105 111
 	if (testID)
106 112
 	{

+ 1
- 0
lib/ios/RNNNavigationOptions.h View File

@@ -34,6 +34,7 @@ extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
34 34
 @property (nonatomic, strong) id orientation;
35 35
 @property (nonatomic, strong) NSNumber* statusBarBlur;
36 36
 @property (nonatomic, strong) NSNumber* statusBarHideWithTopBar;
37
+@property (nonatomic, strong) NSString* statusBarStyle;
37 38
 @property (nonatomic, strong) NSNumber* popGesture;
38 39
 @property (nonatomic, strong) UIImage* backgroundImage;
39 40
 @property (nonatomic, strong) UIImage* rootBackgroundImage;

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

@@ -20,6 +20,8 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
20 20
 -(instancetype)initWithDict:(NSDictionary *)options {
21 21
 	self = [super init];
22 22
 	self.statusBarHidden = [options objectForKey:@"statusBarHidden"];
23
+	self.statusBarBlur = [options objectForKey:@"statusBarBlur"];
24
+	self.statusBarStyle = [options objectForKey:@"statusBarStyle"];
23 25
 	self.screenBackgroundColor = [options objectForKey:@"screenBackgroundColor"];
24 26
 	self.backButtonTransition = [options objectForKey:@"backButtonTransition"];
25 27
 	self.orientation = [options objectForKey:@"orientation"];
@@ -51,9 +53,7 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
51 53
 	}
52 54
 }
53 55
 
54
--(void)applyOn:(UIViewController*)viewController {
55
-//	[_defaultOptions applyOn:viewController];
56
-	
56
+-(void)applyOn:(UIViewController*)viewController {	
57 57
 	[self.topBar applyOn:viewController];
58 58
 	[self.bottomTabs applyOn:viewController];
59 59
 	[self.topTab applyOn:viewController];

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

@@ -17,7 +17,12 @@
17 17
 -(void)mergeWith:(NSDictionary *)otherOptions {
18 18
 	for (id key in otherOptions) {
19 19
 		if ([self hasProperty:key]) {
20
-			[self setValue:[otherOptions objectForKey:key] forKey:key];
20
+			if ([[self valueForKey:key] isKindOfClass:[RNNOptions class]]) {
21
+				RNNOptions* options = [self valueForKey:key];
22
+				[options mergeWith:[otherOptions objectForKey:key]];
23
+			} else {
24
+				[self setValue:[otherOptions objectForKey:key] forKey:key];
25
+			}
21 26
 		}
22 27
 	}
23 28
 }

+ 25
- 6
lib/ios/RNNRootViewController.m View File

@@ -44,8 +44,10 @@
44 44
 -(void)viewWillAppear:(BOOL)animated{
45 45
 	[super viewWillAppear:animated];
46 46
 	[self.options applyOn:self];
47
+	
47 48
 	[self setCustomNavigationTitleView];
48 49
 	[self setCustomNavigationBarView];
50
+	[self setCustomNavigationComponentBackground];
49 51
 }
50 52
 
51 53
 -(void)viewDidAppear:(BOOL)animated {
@@ -71,23 +73,32 @@
71 73
 }
72 74
 
73 75
 - (void)setCustomNavigationTitleView {
74
-	if (self.options.topBar.customTitleViewName) {
75
-		UIView *reactView = [_creator createRootView:self.options.topBar.customTitleViewName rootViewId:self.options.topBar.customTitleViewName];
76
+	if (self.options.topBar.title.component) {
77
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.title.component rootViewId:self.options.topBar.title.component];
76 78
 		
77
-		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:nil];
79
+		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:self.options.topBar.title.componentAlignment];
78 80
 		self.navigationItem.titleView = titleView;
79 81
 	}
80 82
 }
81 83
 
82 84
 - (void)setCustomNavigationBarView {
83
-	if (self.options.topBar.customViewName) {
84
-		UIView *reactView = [_creator createRootView:self.options.topBar.customViewName rootViewId:@"navBar"];
85
+	if (self.options.topBar.componentName) {
86
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.componentName rootViewId:@"navBar"];
85 87
 		
86
-		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:nil];
88
+		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
87 89
 		[self.navigationController.navigationBar addSubview:titleView];
88 90
 	}
89 91
 }
90 92
 
93
+- (void)setCustomNavigationComponentBackground {
94
+	if (self.options.topBar.backgroundComponentName) {
95
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.backgroundComponentName rootViewId:@"navBarBackground"];
96
+		
97
+		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
98
+		[self.navigationController.navigationBar insertSubview:titleView atIndex:1];
99
+	}
100
+}
101
+
91 102
 -(BOOL)isCustomTransitioned {
92 103
 	return self.options.customTransition.animations != nil;
93 104
 }
@@ -109,6 +120,14 @@
109 120
 	return NO;
110 121
 }
111 122
 
123
+- (UIStatusBarStyle)preferredStatusBarStyle {
124
+	if (self.options.statusBarStyle && [self.options.statusBarStyle isEqualToString:@"light"]) {
125
+		return UIStatusBarStyleLightContent;
126
+	} else {
127
+		return UIStatusBarStyleDefault;
128
+	}
129
+}
130
+
112 131
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
113 132
 	return self.options.supportedOrientations;
114 133
 }

+ 13
- 0
lib/ios/RNNTitleOptions.h View File

@@ -0,0 +1,13 @@
1
+#import "RNNOptions.h"
2
+
3
+@interface RNNTitleOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) NSString* text;
6
+@property (nonatomic, strong) NSNumber* fontSize;
7
+@property (nonatomic, strong) NSNumber* color;
8
+@property (nonatomic, strong) NSString* fontFamily;
9
+@property (nonatomic, strong) NSString* component;
10
+@property (nonatomic, strong) NSString* componentAlignment;
11
+
12
+
13
+@end

+ 32
- 0
lib/ios/RNNTitleOptions.m View File

@@ -0,0 +1,32 @@
1
+#import "RNNTitleOptions.h"
2
+
3
+@implementation RNNTitleOptions
4
+
5
+- (void)applyOn:(UIViewController *)viewController {
6
+	if (self.text) {
7
+		viewController.navigationItem.title = self.text;
8
+	}
9
+	
10
+	if (self.fontFamily || self.fontSize || self.color) {
11
+		NSMutableDictionary* navigationBarTitleTextAttributes = [NSMutableDictionary new];
12
+		if (self.color) {
13
+			navigationBarTitleTextAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:[self valueForKey:@"color"]];
14
+		}
15
+		if (self.fontFamily){
16
+			if(self.fontSize) {
17
+				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.fontFamily size:[self.fontSize floatValue]];
18
+			} else {
19
+				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.fontFamily size:20];
20
+			}
21
+		} else if (self.fontSize) {
22
+			navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont systemFontOfSize:[self.fontSize floatValue]];
23
+		}
24
+		viewController.navigationController.navigationBar.titleTextAttributes = navigationBarTitleTextAttributes;
25
+		if (@available(iOS 11.0, *)){
26
+			viewController.navigationController.navigationBar.largeTitleTextAttributes = navigationBarTitleTextAttributes;
27
+		}
28
+		
29
+	}
30
+}
31
+
32
+@end

+ 8
- 6
lib/ios/RNNTopBarOptions.h View File

@@ -1,27 +1,29 @@
1 1
 #import "RNNOptions.h"
2
+#import "RNNTitleOptions.h"
2 3
 
3 4
 @interface RNNTopBarOptions : RNNOptions
4 5
 
5 6
 @property (nonatomic, strong) NSArray* leftButtons;
6 7
 @property (nonatomic, strong) NSArray* rightButtons;
7 8
 @property (nonatomic, strong) NSNumber* backgroundColor;
8
-@property (nonatomic, strong) NSNumber* textColor;
9
-@property (nonatomic, strong) NSString* title;
10
-@property (nonatomic, strong) NSString* textFontFamily;
11 9
 @property (nonatomic, strong) NSNumber* visible;
12 10
 @property (nonatomic, strong) NSNumber* hideOnScroll;
13 11
 @property (nonatomic, strong) NSNumber* buttonColor;
14 12
 @property (nonatomic, strong) NSNumber* translucent;
15 13
 @property (nonatomic, strong) NSNumber* transparent;
16 14
 @property (nonatomic, strong) NSNumber* drawBehind;
17
-@property (nonatomic, strong) NSNumber* textFontSize;
18 15
 @property (nonatomic, strong) NSNumber* noBorder;
19 16
 @property (nonatomic, strong) NSNumber* blur;
20 17
 @property (nonatomic, strong) NSNumber* animate;
21 18
 @property (nonatomic, strong) NSNumber* largeTitle;
22 19
 @property (nonatomic, strong) NSString* testID;
20
+@property (nonatomic, strong) RNNTitleOptions* title;
21
+@property (nonatomic, strong) NSNumber* backButtonImage;
22
+@property (nonatomic, strong) NSNumber* backButtonHidden;
23
+@property (nonatomic, strong) NSString* backButtonTitle;
24
+@property (nonatomic, strong) NSNumber* hideBackButtonTitle;
23 25
 
24
-@property (nonatomic, strong) NSString* customTitleViewName;
25
-@property (nonatomic, strong) NSString* customViewName;
26
+@property (nonatomic, strong) NSString* componentName;
27
+@property (nonatomic, strong) NSString* backgroundComponentName;
26 28
 
27 29
 @end

+ 37
- 28
lib/ios/RNNTopBarOptions.m View File

@@ -13,18 +13,22 @@ extern const NSInteger BLUR_TOPBAR_TAG;
13 13
 
14 14
 @implementation RNNTopBarOptions
15 15
 
16
+- (instancetype)initWithDict:(NSDictionary *)dict {
17
+	self = [super initWithDict:dict];
18
+	self.title = [RNNTitleOptions new];
19
+
20
+	return self;
21
+}
22
+
16 23
 - (void)applyOn:(UIViewController*)viewController {
24
+	[self.title applyOn:viewController];
17 25
 	if (self.backgroundColor) {
18 26
 		UIColor* backgroundColor = [RCTConvert UIColor:self.backgroundColor];
19 27
 		viewController.navigationController.navigationBar.barTintColor = backgroundColor;
20 28
 	} else {
21 29
 		viewController.navigationController.navigationBar.barTintColor = nil;
22 30
 	}
23
-	
24
-	if (self.title) {
25
-		viewController.navigationItem.title = self.title;
26
-	}
27
-	
31
+
28 32
 	if (@available(iOS 11.0, *)) {
29 33
 		if (self.largeTitle){
30 34
 			if ([self.largeTitle boolValue]) {
@@ -39,35 +43,16 @@ extern const NSInteger BLUR_TOPBAR_TAG;
39 43
 		}
40 44
 	}
41 45
 	
42
-	
43
-	if (self.textFontFamily || self.textFontSize || self.textColor) {
44
-		NSMutableDictionary* navigationBarTitleTextAttributes = [NSMutableDictionary new];
45
-		if (self.textColor) {
46
-			navigationBarTitleTextAttributes[NSForegroundColorAttributeName] = [RCTConvert UIColor:[self valueForKey:@"textColor"]];
47
-		}
48
-		if (self.textFontFamily){
49
-			if(self.textFontSize) {
50
-				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.textFontFamily size:[self.textFontSize floatValue]];
51
-			} else {
52
-				navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont fontWithName:self.textFontFamily size:20];
53
-			}
54
-		} else if (self.textFontSize) {
55
-			navigationBarTitleTextAttributes[NSFontAttributeName] = [UIFont systemFontOfSize:[self.textFontSize floatValue]];
56
-		}
57
-		viewController.navigationController.navigationBar.titleTextAttributes = navigationBarTitleTextAttributes;
58
-		if (@available(iOS 11.0, *)){
59
-			viewController.navigationController.navigationBar.largeTitleTextAttributes = navigationBarTitleTextAttributes;
60
-		}
61
-		
62
-	}
63
-	
64
-	
65 46
 	if (self.visible) {
66 47
 		[viewController.navigationController setNavigationBarHidden:![self.visible boolValue] animated:[self.animate boolValue]];
48
+	} else {
49
+		[viewController.navigationController setNavigationBarHidden:NO animated:NO];
67 50
 	}
68 51
 	
69 52
 	if (self.hideOnScroll) {
70 53
 		viewController.navigationController.hidesBarsOnSwipe = [self.hideOnScroll boolValue];
54
+	} else {
55
+		viewController.navigationController.hidesBarsOnSwipe = NO;
71 56
 	}
72 57
 	
73 58
 	if (self.buttonColor) {
@@ -128,6 +113,8 @@ extern const NSInteger BLUR_TOPBAR_TAG;
128 113
 	
129 114
 	if (self.translucent) {
130 115
 		viewController.navigationController.navigationBar.translucent = [self.translucent boolValue];
116
+	} else {
117
+		viewController.navigationController.navigationBar.translucent = NO;
131 118
 	}
132 119
 	
133 120
 	if (self.drawBehind) {
@@ -136,6 +123,8 @@ extern const NSInteger BLUR_TOPBAR_TAG;
136 123
 		} else {
137 124
 			viewController.edgesForExtendedLayout &= ~UIRectEdgeTop;
138 125
 		}
126
+	} else {
127
+		viewController.edgesForExtendedLayout = UIRectEdgeAll;
139 128
 	}
140 129
 	
141 130
 	if (self.noBorder) {
@@ -156,6 +145,26 @@ extern const NSInteger BLUR_TOPBAR_TAG;
156 145
 		_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:(RNNRootViewController*)viewController];
157 146
 		[_navigationButtons applyLeftButtons:self.leftButtons rightButtons:self.rightButtons];
158 147
 	}
148
+	
149
+	UIImage *image = self.backButtonImage ? [RCTConvert UIImage:self.backButtonImage] : nil;
150
+	[[UINavigationBar appearance] setBackIndicatorImage:image];
151
+	[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:image];
152
+	
153
+	if (self.hideBackButtonTitle) {
154
+		self.backButtonTitle = @"";
155
+	}
156
+	
157
+	if (self.backButtonTitle) {
158
+		UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:self.backButtonTitle
159
+																	 style:UIBarButtonItemStylePlain
160
+																	target:nil
161
+																	action:nil];
162
+		
163
+		viewController.navigationItem.backBarButtonItem = backItem;
164
+	}
165
+	
166
+	viewController.navigationItem.hidesBackButton = [self.backButtonHidden boolValue];
167
+	
159 168
 }
160 169
 
161 170
 -(void)storeOriginalTopBarImages:(UIViewController*)viewController {

+ 8
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

@@ -77,6 +77,8 @@
77 77
 		504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */; };
78 78
 		504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */; };
79 79
 		504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */; };
80
+		50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50570B242061473D006A1B5C /* RNNTitleOptions.h */; };
81
+		50570B272061473D006A1B5C /* RNNTitleOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50570B252061473D006A1B5C /* RNNTitleOptions.m */; };
80 82
 		50762D08205E96C200E3D18A /* RNNModalAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50762D06205E96C200E3D18A /* RNNModalAnimation.h */; };
81 83
 		50762D09205E96C200E3D18A /* RNNModalAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 50762D07205E96C200E3D18A /* RNNModalAnimation.m */; };
82 84
 		507E7D57201DDD3000444E6C /* RNNAnimationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 507E7D55201DDD3000444E6C /* RNNAnimationOptions.h */; };
@@ -260,6 +262,8 @@
260 262
 		504AFE631FFE53070076E904 /* RNNOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNOptions.m; sourceTree = "<group>"; };
261 263
 		504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabsOptions.h; sourceTree = "<group>"; };
262 264
 		504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabsOptions.m; sourceTree = "<group>"; };
265
+		50570B242061473D006A1B5C /* RNNTitleOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTitleOptions.h; sourceTree = "<group>"; };
266
+		50570B252061473D006A1B5C /* RNNTitleOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTitleOptions.m; sourceTree = "<group>"; };
263 267
 		50762D06205E96C200E3D18A /* RNNModalAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNModalAnimation.h; sourceTree = "<group>"; };
264 268
 		50762D07205E96C200E3D18A /* RNNModalAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNModalAnimation.m; sourceTree = "<group>"; };
265 269
 		507E7D55201DDD3000444E6C /* RNNAnimationOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNAnimationOptions.h; sourceTree = "<group>"; };
@@ -477,6 +481,8 @@
477 481
 				E83BAD6A1F27363A00A9F3DD /* RNNNavigationOptions.m */,
478 482
 				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
479 483
 				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
484
+				50570B242061473D006A1B5C /* RNNTitleOptions.h */,
485
+				50570B252061473D006A1B5C /* RNNTitleOptions.m */,
480 486
 				A7626BFF1FC578AB00492FB8 /* RNNBottomTabsOptions.h */,
481 487
 				A7626C001FC5796200492FB8 /* RNNBottomTabsOptions.m */,
482 488
 				50EB933F1FE14A3E00BD8EEE /* RNNBottomTabOptions.h */,
@@ -754,6 +760,7 @@
754 760
 				263905B31E4C6F440023D7D3 /* MMDrawerVisualState.h in Headers */,
755 761
 				50451D092042E20600695F00 /* RNNTransitionsOptions.h in Headers */,
756 762
 				E8A5CD621F49114F00E89D0D /* RNNElement.h in Headers */,
763
+				50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */,
757 764
 				504AFE741FFFF0540076E904 /* RNNTopTabsOptions.h in Headers */,
758 765
 				E8E5182E1F83A48B000467AC /* RNNTransitionStateHolder.h in Headers */,
759 766
 				507E7D57201DDD3000444E6C /* RNNAnimationOptions.h in Headers */,
@@ -888,6 +895,7 @@
888 895
 				261F0E6B1E6F028A00989DE2 /* RNNNavigationStackManager.m in Sources */,
889 896
 				5016E8F020209690009D4F7C /* RNNCustomTitleView.m in Sources */,
890 897
 				E8DA24411F97459B00CD552B /* RNNElementFinder.m in Sources */,
898
+				50570B272061473D006A1B5C /* RNNTitleOptions.m in Sources */,
891 899
 				263905BF1E4C6F440023D7D3 /* RCCTheSideBarManagerViewController.m in Sources */,
892 900
 				7B1126A01E2D263F00F9B03B /* RNNEventEmitter.m in Sources */,
893 901
 				A7626BFD1FC2FB2C00492FB8 /* RNNTopBarOptions.m in Sources */,

+ 1
- 1
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

@@ -63,7 +63,7 @@
63 63
 
64 64
 -(void)testDynamicStylesMergeWithStaticStyles {
65 65
 	RNNNavigationOptions* initialOptions = [[RNNNavigationOptions alloc] init];
66
-	[initialOptions.topBar setTitle:@"the title"];
66
+	initialOptions.topBar.title.text = @"the title";
67 67
 	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithName:@"name"
68 68
 																withOptions:initialOptions
69 69
 															withComponentId:@"componentId"

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

@@ -22,9 +22,9 @@
22 22
 
23 23
 - (void)testChangeRNNNavigationOptionsDynamically {
24 24
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:@{@"topBar": @{@"backgroundColor" : @(0xff0000ff)}}];
25
-	NSDictionary* dynamicOptions = @{@"topBar": @{@"textColor" : @(0xffff00ff), @"title" : @"hello"}};
25
+	NSDictionary* dynamicOptions = @{@"topBar": @{@"textColor" : @(0xffff00ff), @"title" : @{@"text": @"hello"}}};
26 26
 	[options mergeWith:dynamicOptions];
27
-	XCTAssertTrue([options.topBar.title isEqual:@"hello"]);
27
+	XCTAssertTrue([options.topBar.title.text isEqual:@"hello"]);
28 28
 }
29 29
 
30 30
 - (void)testChangeRNNNavigationOptionsWithInvalidProperties {

+ 12
- 12
lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m View File

@@ -106,7 +106,7 @@
106 106
 
107 107
 -(void)testTitle_string{
108 108
 	NSString* title =@"some title";
109
-	self.options.topBar.title = title;
109
+	self.options.topBar.title.text = title;
110 110
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
111 111
 	
112 112
 	[self.uut viewWillAppear:false];
@@ -122,7 +122,7 @@
122 122
 
123 123
 -(void)testTopBarTextColor_validColor{
124 124
 	NSNumber* inputColor = @(0xFFFF0000);
125
-	self.options.topBar.textColor = inputColor;
125
+	self.options.topBar.title.color = inputColor;
126 126
 	__unused UINavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
127 127
 	[self.uut viewWillAppear:false];
128 128
 	UIColor* expectedColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
@@ -156,7 +156,7 @@
156 156
 -(void)testTopBarTextFontFamily_validFont{
157 157
 	NSString* inputFont = @"HelveticaNeue";
158 158
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
159
-	self.options.topBar.textFontFamily = inputFont;
159
+	self.options.topBar.title.fontFamily = inputFont;
160 160
 	[self.uut viewWillAppear:false];
161 161
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:20];
162 162
 	XCTAssertTrue([self.uut.navigationController.navigationBar.titleTextAttributes[@"NSFont"] isEqual:expectedFont]);
@@ -227,7 +227,7 @@
227 227
 
228 228
 -(void)testTopBarTextFontSize_withoutTextFontFamily_withoutTextColor {
229 229
 	NSNumber* topBarTextFontSizeInput = @(15);
230
-	self.options.topBar.textFontSize = topBarTextFontSizeInput;
230
+	self.options.topBar.title.fontSize = topBarTextFontSizeInput;
231 231
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
232 232
 	[self.uut viewWillAppear:false];
233 233
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
@@ -237,8 +237,8 @@
237 237
 -(void)testTopBarTextFontSize_withoutTextFontFamily_withTextColor {
238 238
 	NSNumber* topBarTextFontSizeInput = @(15);
239 239
 	NSNumber* inputColor = @(0xFFFF0000);
240
-	self.options.topBar.textFontSize = topBarTextFontSizeInput;
241
-	self.options.topBar.textColor = inputColor;
240
+	self.options.topBar.title.fontSize = topBarTextFontSizeInput;
241
+	self.options.topBar.title.color = inputColor;
242 242
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
243 243
 	[self.uut viewWillAppear:false];
244 244
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
@@ -251,9 +251,9 @@
251 251
 	NSNumber* topBarTextFontSizeInput = @(15);
252 252
 	NSNumber* inputColor = @(0xFFFF0000);
253 253
 	NSString* inputFont = @"HelveticaNeue";
254
-	self.options.topBar.textFontSize = topBarTextFontSizeInput;
255
-	self.options.topBar.textColor = inputColor;
256
-	self.options.topBar.textFontFamily = inputFont;
254
+	self.options.topBar.title.fontSize = topBarTextFontSizeInput;
255
+	self.options.topBar.title.color = inputColor;
256
+	self.options.topBar.title.fontFamily = inputFont;
257 257
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
258 258
 	[self.uut viewWillAppear:false];
259 259
 	UIColor* expectedColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:1];
@@ -265,8 +265,8 @@
265 265
 -(void)testTopBarTextFontSize_withTextFontFamily_withoutTextColor {
266 266
 	NSNumber* topBarTextFontSizeInput = @(15);
267 267
 	NSString* inputFont = @"HelveticaNeue";
268
-	self.options.topBar.textFontSize = topBarTextFontSizeInput;
269
-	self.options.topBar.textFontFamily = inputFont;
268
+	self.options.topBar.title.fontSize = topBarTextFontSizeInput;
269
+	self.options.topBar.title.fontFamily = inputFont;
270 270
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
271 271
 	[self.uut viewWillAppear:false];
272 272
 	UIFont* expectedFont = [UIFont fontWithName:inputFont size:15];
@@ -277,7 +277,7 @@
277 277
 -(void)testTopBarTextFontFamily_invalidFont{
278 278
 	NSString* inputFont = @"HelveticaNeueeeee";
279 279
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
280
-	self.options.topBar.textFontFamily = inputFont;
280
+	self.options.topBar.title.fontFamily = inputFont;
281 281
 	//	XCTAssertThrows([self.uut viewWillAppear:false]);
282 282
 }
283 283
 

+ 4
- 2
lib/src/commands/LayoutTreeParser.test.ts View File

@@ -124,7 +124,7 @@ describe('LayoutTreeParser', () => {
124 124
       expect(result.children[1].children[0].children[2].children[0].type).toEqual('TopTabs');
125 125
       expect(result.children[1].children[0].children[2].children[0].children[2].type).toEqual('TopTabs');
126 126
       expect(result.children[1].children[0].children[2].children[0].children[2].children[4].type).toEqual('Stack');
127
-      expect(result.children[1].children[0].children[2].children[0].children[2].data).toEqual({ options: { topBar: { title: 'Hello1' } } });
127
+      expect(result.children[1].children[0].children[2].children[0].children[2].data).toEqual({ options: { topBar: { title: { text: 'Hello1'} } } });
128 128
     });
129 129
   });
130 130
 
@@ -163,7 +163,9 @@ const passProps = {
163 163
 
164 164
 const options = {
165 165
   topBar: {
166
-    title: 'Hello1'
166
+    title: {
167
+      text: 'Hello1'
168
+    }
167 169
   }
168 170
 };
169 171
 

+ 3
- 1
playground/src/screens/BackHandlerModalScreen.js View File

@@ -7,7 +7,9 @@ class BackHandlerModalScreen extends Component {
7 7
   static get options() {
8 8
     return {
9 9
       topBar: {
10
-        title: 'Back Handler'
10
+        title: {
11
+          text: 'Back Handler'
12
+        }
11 13
       }
12 14
     };
13 15
   }

+ 5
- 3
playground/src/screens/BackHandlerScreen.js View File

@@ -7,9 +7,11 @@ class BackHandlerScreen extends Component {
7 7
   static get options() {
8 8
     return {
9 9
       topBar: {
10
-        title: 'Back Handler',
11
-        textColor: 'black',
12
-        textFontSize: 16
10
+        title: {
11
+          text: 'Back Handler',
12
+          color: 'black',
13
+          fontSize: 16
14
+        }
13 15
       }
14 16
     };
15 17
   }

+ 4
- 8
playground/src/screens/CustomTopBar.js View File

@@ -19,7 +19,7 @@ class CustomTopBar extends Component {
19 19
   render() {
20 20
     return (
21 21
       <View style={styles.container}>
22
-        <TouchableOpacity stye={styles.button} onPress={() => Alert.alert(this.props.title, 'Thanks for that :)')}>
22
+        <TouchableOpacity onPress={() => Alert.alert(this.props.title, 'Thanks for that :)')}>
23 23
           <Text style={styles.text}>Press Me</Text>
24 24
         </TouchableOpacity>
25 25
       </View>
@@ -32,16 +32,12 @@ module.exports = CustomTopBar;
32 32
 const styles = StyleSheet.create({
33 33
   container: {
34 34
     flex: 1,
35
+    flexDirection: 'column',
35 36
     justifyContent: 'center',
36
-    alignItems: 'center',
37
-    backgroundColor: 'white'
38
-  },
39
-  button: {
40
-    alignSelf: 'center',
41
-    backgroundColor: 'green'
37
+    alignSelf: 'center'
42 38
   },
43 39
   text: {
44 40
     alignSelf: 'center',
45
-    color: Platform.OS === 'ios' ? 'black' : 'white'
41
+    color: 'black',
46 42
   }
47 43
 });

+ 4
- 2
playground/src/screens/CustomTransitionDestination.js View File

@@ -14,8 +14,10 @@ class CustomTransitionDestination extends Component {
14 14
     return {
15 15
       backButtonTransition: 'custom',
16 16
       topBar: {
17
-        title: 'ye babyyyyyy',
18
-        textFontFamily: 'HelveticaNeue-Italic',
17
+        title: {
18
+          text: 'ye babyyyyyy',
19
+          fontFamily: 'HelveticaNeue-Italic'
20
+        },
19 21
         largeTitle: true
20 22
       }
21 23
     };

+ 4
- 2
playground/src/screens/CustomTransitionOrigin.js View File

@@ -11,8 +11,10 @@ class CustomTransitionOrigin extends Component {
11 11
   static get options() {
12 12
     return {
13 13
       topBar: {
14
-        textFontFamily: 'HelveticaNeue-Italic',
15
-        textFontSize: 16,
14
+        title: {
15
+          fontFamily: 'HelveticaNeue-Italic',
16
+          fontSize: 16
17
+        },
16 18
         largeTitle: false,
17 19
         translucent: true
18 20
       }

+ 33
- 18
playground/src/screens/OptionsScreen.js View File

@@ -17,23 +17,25 @@ class OptionsScreen extends Component {
17 17
   static get options() {
18 18
     return {
19 19
       topBar: {
20
-        title: 'Static Title',
21
-        textColor: 'black',
20
+        title: {
21
+          text: 'Static Title',
22
+          color: 'black',
23
+          fontSize: 16,
24
+          fontFamily: 'HelveticaNeue-Italic',
25
+          largeTitle: false
26
+        },
22 27
         ...Platform.select({
23 28
           android: { drawBehind: true },
24 29
           ios: { drawBehind: false, }
25 30
         }),
26
-        largeTitle: false,
27 31
         visible: true,
28
-        textFontSize: 16,
29
-        textFontFamily: 'HelveticaNeue-Italic',
30 32
         testID: testIDs.TOP_BAR_ELEMENT,
31 33
         rightButtons: [
32
-          {
33
-            id: CUSTOM_BUTTON,
34
-            testID: CUSTOM_BUTTON,
35
-            component: 'CustomTextButton'
36
-          },
34
+          // {
35
+          //   id: CUSTOM_BUTTON,
36
+          //   testID: CUSTOM_BUTTON,
37
+          //   component: 'CustomTextButton'
38
+          // },
37 39
           {
38 40
             id: CUSTOM_BUTTON2,
39 41
             testID: CUSTOM_BUTTON2,
@@ -97,6 +99,7 @@ class OptionsScreen extends Component {
97 99
   render() {
98 100
     return (
99 101
       <View style={styles.root}>
102
+        <View style={{width: 2, height: 2, backgroundColor: 'red', alignSelf: 'center'}}/>
100 103
         <Text style={styles.h1} testID={testIDs.OPTIONS_SCREEN_HEADER}>{`Options Screen`}</Text>
101 104
         <Button title='Dynamic Options' testID={testIDs.DYNAMIC_OPTIONS_BUTTON} onPress={this.onClickDynamicOptions} />
102 105
         <Button title='Show Top Bar' testID={testIDs.SHOW_TOP_BAR_BUTTON} onPress={this.onClickShowTopBar} />
@@ -105,12 +108,11 @@ class OptionsScreen extends Component {
105 108
         <Button title='Top Bar Opaque' onPress={this.onClickTopBarOpaque} />
106 109
         <Button title='scrollView Screen' testID={testIDs.SCROLLVIEW_SCREEN_BUTTON} onPress={this.onClickScrollViewScreen} />
107 110
         <Button title='Custom Transition' testID={testIDs.CUSTOM_TRANSITION_BUTTON} onPress={this.onClickCustomTranstition} />
108
-        {Platform.OS === 'android' ?
109
-          <Button title='Hide fab' testID={testIDs.HIDE_FAB} onPress={this.onClickFab} />
110
-          : null}
111
+        {Platform.OS === 'android' ? <Button title='Hide fab' testID={testIDs.HIDE_FAB} onPress={this.onClickFab} /> : null}
111 112
         <Button title='Show overlay' testID={testIDs.SHOW_OVERLAY_BUTTON} onPress={() => this.onClickShowOverlay(true)} />
112 113
         <Button title='Show touch through overlay' testID={testIDs.SHOW_TOUCH_THROUGH_OVERLAY_BUTTON} onPress={() => this.onClickShowOverlay(false)} />
113 114
         <Button title='Push Default Options Screen' testID={testIDs.PUSH_DEFAULT_OPTIONS_BUTTON} onPress={this.onClickPushDefaultOptionsScreen} />
115
+        <Button title='Show TopBar react view' testID={testIDs.SHOW_TOPBAR_REACT_VIEW} onPress={this.onShowTopBarReactView} />
114 116
         <Text style={styles.footer}>{`this.props.containerId = ${this.props.containerId}`}</Text>
115 117
       </View>
116 118
     );
@@ -161,12 +163,14 @@ class OptionsScreen extends Component {
161 163
   onClickDynamicOptions() {
162 164
     Navigation.setOptions(this.props.componentId, {
163 165
       topBar: {
164
-        title: 'Dynamic Title',
165
-        textColor: '#00FFFF',
166
-        largeTitle: false,
166
+        title: {
167
+          text: 'Dynamic Title',
168
+          color: '#00FFFF',
169
+          largeTitle: false,
170
+          fontSize: 20,
171
+          fontFamily: 'HelveticaNeue-CondensedBold'
172
+        },
167 173
         buttonColor: 'red',
168
-        textFontSize: 20,
169
-        textFontFamily: 'HelveticaNeue-CondensedBold'
170 174
       }
171 175
     });
172 176
   }
@@ -256,6 +260,17 @@ class OptionsScreen extends Component {
256 260
       }
257 261
     });
258 262
   }
263
+
264
+  onShowTopBarReactView = () => {
265
+    Navigation.setOptions(this.props.componentId, {
266
+      topBar: {
267
+        title: {
268
+          component: 'navigation.playground.CustomTopBar',
269
+          alignment: 'center'
270
+        }
271
+      }
272
+    });
273
+  }
259 274
 }
260 275
 
261 276
 const styles = {

+ 3
- 1
playground/src/screens/PushedScreen.js View File

@@ -52,7 +52,9 @@ class PushedScreen extends Component {
52 52
         },
53 53
         options: {
54 54
           topBar: {
55
-            title: `Pushed ${this.getStackPosition() + 1}`
55
+            title: {
56
+              text: `Pushed ${this.getStackPosition() + 1}`
57
+            }
56 58
           }
57 59
         }
58 60
       }

+ 5
- 3
playground/src/screens/ScrollViewScreen.js View File

@@ -12,10 +12,12 @@ class ScrollViewScreen extends Component {
12 12
   static get options() {
13 13
     return {
14 14
       topBar: {
15
-        title: 'Collapse',
15
+        title: {
16
+          text: 'Collapse',
17
+          color: 'black',
18
+          fontSize: 16
19
+        },
16 20
         drawBehind: true,
17
-        textColor: 'black',
18
-        textFontSize: 16,
19 21
         visible: true,
20 22
         testID: testIDs.TOP_BAR_ELEMENT
21 23
       },

+ 11
- 7
playground/src/screens/TopTabOptionsScreen.js View File

@@ -8,9 +8,11 @@ class TopTabOptionsScreen extends PureComponent {
8 8
   static get options() {
9 9
     return {
10 10
       topBar: {
11
-        textColor: 'black',
12
-        textFontSize: 16,
13
-        textFontFamily: 'HelveticaNeue-Italic'
11
+        title: {
12
+          color: 'black',
13
+          fontSize: 16,
14
+          fontFamily: 'HelveticaNeue-Italic'
15
+        }
14 16
       }
15 17
     };
16 18
   }
@@ -33,12 +35,14 @@ class TopTabOptionsScreen extends PureComponent {
33 35
   onClickDynamicOptions() {
34 36
     Navigation.setOptions(this.props.componentId, {
35 37
       topBar: {
36
-        title: 'Dynamic Title',
37
-        textColor: '#00FFFF',
38
+        title: {
39
+          text: 'Dynamic Title',
40
+          color: '#00FFFF',
41
+          fontSize: 16,
42
+          fontFamily: 'HelveticaNeue-CondensedBold'
43
+        },
38 44
         largeTitle: false,
39 45
         buttonColor: 'red',
40
-        textFontSize: 20,
41
-        textFontFamily: 'HelveticaNeue-CondensedBold'
42 46
       }
43 47
     });
44 48
   }

+ 5
- 3
playground/src/screens/TopTabScreen.js View File

@@ -8,9 +8,11 @@ class TopTabScreen extends PureComponent {
8 8
   static get options() {
9 9
     return {
10 10
       topBar: {
11
-        textColor: 'black',
12
-        textFontSize: 16,
13
-        textFontFamily: 'HelveticaNeue-Italic'
11
+        title: {
12
+          color: 'black',
13
+          fontSize: 16,
14
+          fontFamily: 'HelveticaNeue-Italic'
15
+        }
14 16
       },
15 17
       fab: {
16 18
         id: FAB,

+ 28
- 9
playground/src/screens/WelcomeScreen.js View File

@@ -10,7 +10,10 @@ class WelcomeScreen extends Component {
10 10
   static get options() {
11 11
     return {
12 12
       topBar: {
13
-        largeTitle: false,
13
+        title: {
14
+          largeTitle: false,
15
+          title: 'My Screen'
16
+        },
14 17
         drawBehind: true,
15 18
         visible: false,
16 19
         animate: false
@@ -57,7 +60,9 @@ class WelcomeScreen extends Component {
57 60
                     options: {
58 61
                       topBar: {
59 62
                         visible: true,
60
-                        title: 'React Native Navigation!'
63
+                        title: {
64
+                          text: 'React Native Navigation!'
65
+                        }
61 66
                       }
62 67
                     }
63 68
                   }
@@ -216,7 +221,9 @@ class WelcomeScreen extends Component {
216 221
         name: 'navigation.playground.PushedScreen',
217 222
         options: {
218 223
           topBar: {
219
-            title: 'pushed'
224
+            title: {
225
+              text: 'pushed'
226
+            }
220 227
           }
221 228
         }
222 229
       }
@@ -232,7 +239,9 @@ class WelcomeScreen extends Component {
232 239
         },
233 240
         options: {
234 241
           topBar: {
235
-            title: 'pushed',
242
+            title: {
243
+              text: 'pushed'
244
+            },
236 245
             visible: true,
237 246
             testID: testIDs.TOP_BAR_ELEMENT
238 247
           }
@@ -299,10 +308,14 @@ class WelcomeScreen extends Component {
299 308
               },
300 309
               options: {
301 310
                 topTab: {
302
-                  title: 'Tab 1'
311
+                  title: {
312
+                    text: 'Tab 1'
313
+                  }
303 314
                 },
304 315
                 topBar: {
305
-                  title: 'One'
316
+                  title: {
317
+                    text: 'One'
318
+                  }
306 319
                 }
307 320
               }
308 321
             }
@@ -320,7 +333,9 @@ class WelcomeScreen extends Component {
320 333
                   titleFontFamily: 'HelveticaNeue-Italic'
321 334
                 },
322 335
                 topBar: {
323
-                  title: 'Two'
336
+                  title: {
337
+                    text: 'Two'
338
+                  }
324 339
                 }
325 340
               }
326 341
             }
@@ -337,7 +352,9 @@ class WelcomeScreen extends Component {
337 352
                   title: 'Tab 3'
338 353
                 },
339 354
                 topBar: {
340
-                  title: 'Three'
355
+                  title: {
356
+                    text: 'Three'
357
+                  }
341 358
                 }
342 359
               }
343 360
             }
@@ -385,7 +402,9 @@ class WelcomeScreen extends Component {
385 402
     });
386 403
     Navigation.setOptions('my unique id', {
387 404
       topBar: {
388
-        title: 'User provided id'
405
+        title: {
406
+          text: 'User provided id'
407
+        }
389 408
       }
390 409
     });
391 410
   }

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

@@ -9,6 +9,7 @@ module.exports = {
9 9
   PUSH_BUTTON: `PUSH_BUTTON`,
10 10
   PUSH_OPTIONS_BUTTON: `PUSH_OPTIONS_BUTTON`,
11 11
   PUSH_DEFAULT_OPTIONS_BUTTON: `PUSH_DEFAULT_OPTIONS_BUTTON`,
12
+  SHOW_TOPBAR_REACT_VIEW: `SHOW_TOPBAR_REACT_VIEW`,
12 13
   BACK_HANDLER_BUTTON: `BACK_HANDLER_BUTTON`,
13 14
   SHOW_MODAL_BUTTON: `SHOW_MODAL_BUTTON`,
14 15
   SHOW_REDBOX_BUTTON: `SHOW_REDBOX_BUTTON`,