Browse Source

RTL Layout (#4575)

* Support rtl layout

* ios layout options

* Support rtl layout

* Use View.LAYOUT_DIRECTION_LTR constant instead of 0

* move layout direction logic to RootPresenter

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* fix tests

* Fix tests
Hadi Mostafapour 5 years ago
parent
commit
d09d0108d1

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

4
 import com.reactnativenavigation.parse.params.NullColor;
4
 import com.reactnativenavigation.parse.params.NullColor;
5
 import com.reactnativenavigation.parse.params.NullNumber;
5
 import com.reactnativenavigation.parse.params.NullNumber;
6
 import com.reactnativenavigation.parse.params.Number;
6
 import com.reactnativenavigation.parse.params.Number;
7
+import com.reactnativenavigation.parse.params.Text;
8
+import com.reactnativenavigation.parse.params.NullText;
7
 import com.reactnativenavigation.parse.parsers.ColorParser;
9
 import com.reactnativenavigation.parse.parsers.ColorParser;
8
 import com.reactnativenavigation.parse.parsers.NumberParser;
10
 import com.reactnativenavigation.parse.parsers.NumberParser;
11
+import com.reactnativenavigation.parse.parsers.TextParser;
12
+
9
 
13
 
10
 import org.json.JSONObject;
14
 import org.json.JSONObject;
11
 
15
 
18
         result.componentBackgroundColor = ColorParser.parse(json, "componentBackgroundColor");
22
         result.componentBackgroundColor = ColorParser.parse(json, "componentBackgroundColor");
19
         result.topMargin = NumberParser.parse(json, "topMargin");
23
         result.topMargin = NumberParser.parse(json, "topMargin");
20
         result.orientation = OrientationOptions.parse(json);
24
         result.orientation = OrientationOptions.parse(json);
25
+        result.direction = TextParser.parse(json, "direction");
21
 
26
 
22
         return result;
27
         return result;
23
     }
28
     }
26
     public Colour componentBackgroundColor = new NullColor();
31
     public Colour componentBackgroundColor = new NullColor();
27
     public Number topMargin = new NullNumber();
32
     public Number topMargin = new NullNumber();
28
     public OrientationOptions orientation = new OrientationOptions();
33
     public OrientationOptions orientation = new OrientationOptions();
34
+    public Text direction = new NullText();
29
 
35
 
30
     public void mergeWith(LayoutOptions other) {
36
     public void mergeWith(LayoutOptions other) {
31
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
37
         if (other.backgroundColor.hasValue()) backgroundColor = other.backgroundColor;
32
         if (other.componentBackgroundColor.hasValue()) componentBackgroundColor = other.componentBackgroundColor;
38
         if (other.componentBackgroundColor.hasValue()) componentBackgroundColor = other.componentBackgroundColor;
33
         if (other.topMargin.hasValue()) topMargin = other.topMargin;
39
         if (other.topMargin.hasValue()) topMargin = other.topMargin;
34
         if (other.orientation.hasValue()) orientation = other.orientation;
40
         if (other.orientation.hasValue()) orientation = other.orientation;
41
+        if (other.direction.hasValue()) direction = other.direction;
35
 
42
 
36
     }
43
     }
37
 
44
 
40
         if (!componentBackgroundColor.hasValue()) componentBackgroundColor = defaultOptions.componentBackgroundColor;
47
         if (!componentBackgroundColor.hasValue()) componentBackgroundColor = defaultOptions.componentBackgroundColor;
41
         if (!topMargin.hasValue()) topMargin = defaultOptions.topMargin;
48
         if (!topMargin.hasValue()) topMargin = defaultOptions.topMargin;
42
         if (!orientation.hasValue()) orientation = defaultOptions.orientation;
49
         if (!orientation.hasValue()) orientation = defaultOptions.orientation;
50
+        if (!direction.hasValue()) direction = defaultOptions.direction;
43
     }
51
     }
44
 }
52
 }

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

67
 		handle(() -> {
67
 		handle(() -> {
68
             navigator().setEventEmitter(eventEmitter);
68
             navigator().setEventEmitter(eventEmitter);
69
             final ViewController viewController = newLayoutFactory().create(layoutTree);
69
             final ViewController viewController = newLayoutFactory().create(layoutTree);
70
-            navigator().setRoot(viewController, new NativeCommandListener(commandId, promise, eventEmitter, now));
70
+            navigator().setRoot(viewController, new NativeCommandListener(commandId, promise, eventEmitter, now), reactInstanceManager);
71
         });
71
         });
72
 	}
72
 	}
73
 
73
 

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

102
     }
102
     }
103
 
103
 
104
     public void applyNavigationIcon(Toolbar toolbar) {
104
     public void applyNavigationIcon(Toolbar toolbar) {
105
-        navigationIconResolver.resolve(button, icon -> {
105
+        Integer direction = getActivity().getWindow().getDecorView().getLayoutDirection();
106
+        navigationIconResolver.resolve(button, direction, icon -> {
106
             setIconColor(icon);
107
             setIconColor(icon);
107
             toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
108
             toolbar.setNavigationOnClickListener(view -> onPressListener.onPress(button.id));
108
             toolbar.setNavigationIcon(icon);
109
             toolbar.setNavigationIcon(icon);

+ 4
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolver.java View File

5
 import android.support.annotation.NonNull;
5
 import android.support.annotation.NonNull;
6
 import android.support.v4.content.ContextCompat;
6
 import android.support.v4.content.ContextCompat;
7
 import android.util.Log;
7
 import android.util.Log;
8
+import android.view.View;
8
 
9
 
9
 import com.reactnativenavigation.R;
10
 import com.reactnativenavigation.R;
10
 import com.reactnativenavigation.parse.params.Button;
11
 import com.reactnativenavigation.parse.params.Button;
23
         this.imageLoader = imageLoader;
24
         this.imageLoader = imageLoader;
24
     }
25
     }
25
 
26
 
26
-    public void resolve(Button button, Func1<Drawable> onSuccess) {
27
+    public void resolve(Button button, Integer direction, Func1<Drawable> onSuccess) {
27
         if (button.icon.hasValue()) {
28
         if (button.icon.hasValue()) {
28
             imageLoader.loadIcon(context, button.icon.get(), new ImageLoadingListenerAdapter() {
29
             imageLoader.loadIcon(context, button.icon.get(), new ImageLoadingListenerAdapter() {
29
                 @Override
30
                 @Override
37
                 }
38
                 }
38
             });
39
             });
39
         } else if (Constants.BACK_BUTTON_ID.equals(button.id)) {
40
         } else if (Constants.BACK_BUTTON_ID.equals(button.id)) {
40
-            onSuccess.run(ContextCompat.getDrawable(context, R.drawable.ic_arrow_back_black_24dp));
41
+            Boolean isRTL = direction == View.LAYOUT_DIRECTION_RTL;
42
+            onSuccess.run(ContextCompat.getDrawable(context, isRTL ? R.drawable.ic_arrow_back_black_rtl_24dp : R.drawable.ic_arrow_back_black_24dp));
41
         } else {
43
         } else {
42
             Log.w("RNN", "Left button needs to have an icon");
44
             Log.w("RNN", "Left button needs to have an icon");
43
         }
45
         }

+ 4
- 2
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java View File

21
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
21
 import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
22
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
22
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
23
 
23
 
24
+import com.facebook.react.ReactInstanceManager;
25
+
24
 import java.util.Collection;
26
 import java.util.Collection;
25
 import java.util.Collections;
27
 import java.util.Collections;
26
 import java.util.List;
28
 import java.util.List;
125
 
127
 
126
     }
128
     }
127
 
129
 
128
-    public void setRoot(final ViewController viewController, CommandListener commandListener) {
130
+    public void setRoot(final ViewController viewController, CommandListener commandListener, ReactInstanceManager reactInstanceManager) {
129
         destroyRoot();
131
         destroyRoot();
130
         final boolean removeSplashView = isRootNotCreated();
132
         final boolean removeSplashView = isRootNotCreated();
131
         if (isRootNotCreated()) getView();
133
         if (isRootNotCreated()) getView();
136
                 if (removeSplashView) removePreviousContentView();
138
                 if (removeSplashView) removePreviousContentView();
137
                 super.onSuccess(childId);
139
                 super.onSuccess(childId);
138
             }
140
             }
139
-        });
141
+        }, reactInstanceManager);
140
     }
142
     }
141
 
143
 
142
     private void removePreviousContentView() {
144
     private void removePreviousContentView() {

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

2
 
2
 
3
 import android.content.Context;
3
 import android.content.Context;
4
 import android.widget.FrameLayout;
4
 import android.widget.FrameLayout;
5
+import android.view.View;
5
 
6
 
6
 import com.reactnativenavigation.anim.NavigationAnimator;
7
 import com.reactnativenavigation.anim.NavigationAnimator;
7
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
9
 import com.reactnativenavigation.viewcontrollers.ViewController;
10
 import com.reactnativenavigation.viewcontrollers.ViewController;
10
 import com.reactnativenavigation.views.element.ElementTransitionManager;
11
 import com.reactnativenavigation.views.element.ElementTransitionManager;
11
 
12
 
13
+import com.facebook.react.modules.i18nmanager.I18nUtil;
14
+import com.facebook.react.bridge.ReactApplicationContext;
15
+import com.facebook.react.ReactInstanceManager;
16
+
12
 public class RootPresenter {
17
 public class RootPresenter {
13
     private NavigationAnimator animator;
18
     private NavigationAnimator animator;
14
     private FrameLayout rootLayout;
19
     private FrameLayout rootLayout;
25
         this.animator = animator;
30
         this.animator = animator;
26
     }
31
     }
27
 
32
 
28
-    void setRoot(ViewController root, Options defaultOptions, CommandListener listener) {
33
+    void setRoot(ViewController root, Options defaultOptions, CommandListener listener, ReactInstanceManager reactInstanceManager) {
34
+        setLayoutDirection(root, defaultOptions, (ReactApplicationContext) reactInstanceManager.getCurrentReactContext());
29
         rootLayout.addView(root.getView());
35
         rootLayout.addView(root.getView());
30
         Options options = root.resolveCurrentOptions(defaultOptions);
36
         Options options = root.resolveCurrentOptions(defaultOptions);
31
         root.setWaitForRender(options.animations.setRoot.waitForRender);
37
         root.setWaitForRender(options.animations.setRoot.waitForRender);
47
             listener.onSuccess(root.getId());
53
             listener.onSuccess(root.getId());
48
         }
54
         }
49
     }
55
     }
56
+
57
+    private void setLayoutDirection(ViewController root, Options defaultOptions, ReactApplicationContext reactContext) {
58
+        if (defaultOptions.layout.direction.hasValue()) {
59
+            I18nUtil i18nUtil = I18nUtil.getInstance();
60
+            Boolean isRtl = defaultOptions.layout.direction.get().equals("rtl");
61
+
62
+            root.getActivity().getWindow().getDecorView().setLayoutDirection(isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
63
+            i18nUtil.allowRTL(reactContext, isRtl);
64
+            i18nUtil.forceRTL(reactContext, isRtl);
65
+        }
66
+    }
50
 }
67
 }

+ 4
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

101
     }
101
     }
102
 
102
 
103
     private void alignTextView(Alignment alignment, TextView view) {
103
     private void alignTextView(Alignment alignment, TextView view) {
104
+        Integer direction = view.getParent().getLayoutDirection();
105
+        Boolean isRTL = direction == View.LAYOUT_DIRECTION_RTL;
104
         int width = view.getResources().getDisplayMetrics().widthPixels;
106
         int width = view.getResources().getDisplayMetrics().widthPixels;
105
         view.post(() -> {
107
         view.post(() -> {
106
             if (alignment == Alignment.Center) {
108
             if (alignment == Alignment.Center) {
108
                 //noinspection IntegerDivisionInFloatingPointContext
110
                 //noinspection IntegerDivisionInFloatingPointContext
109
                 view.setX((width - view.getWidth()) / 2);
111
                 view.setX((width - view.getWidth()) / 2);
110
             } else if (leftButtonController != null) {
112
             } else if (leftButtonController != null) {
111
-                view.setX(getContentInsetStartWithNavigation());
113
+                view.setX(isRTL ? (getWidth() - view.getWidth()) - getContentInsetStartWithNavigation() : getContentInsetStartWithNavigation());
112
             } else {
114
             } else {
113
-                view.setX(UiUtils.dpToPx(getContext(), DEFAULT_LEFT_MARGIN));
115
+                view.setX(isRTL ? (getWidth() - view.getWidth()) - UiUtils.dpToPx(getContext(), DEFAULT_LEFT_MARGIN) : UiUtils.dpToPx(getContext(), DEFAULT_LEFT_MARGIN));
114
             }
116
             }
115
         });
117
         });
116
     }
118
     }

+ 9
- 0
lib/android/app/src/main/res/drawable/ic_arrow_back_black_rtl_24dp.xml View File

1
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+    android:width="24dp"
3
+    android:height="24dp"
4
+    android:viewportHeight="24.0"
5
+    android:viewportWidth="24.0">
6
+    <path
7
+        android:fillColor="#000000"
8
+        android:pathData="M4,13H16.15l-5.6,5.6L12,20l8-8L12,4,10.6,5.4,16.15,11H4Z" />
9
+</vector>

+ 3
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/button/NavigationIconResolverTest.java View File

3
 import android.content.Context;
3
 import android.content.Context;
4
 import android.graphics.Color;
4
 import android.graphics.Color;
5
 import android.graphics.drawable.Drawable;
5
 import android.graphics.drawable.Drawable;
6
+import android.view.View;
6
 
7
 
7
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.BaseTest;
8
 import com.reactnativenavigation.mocks.ImageLoaderMock;
9
 import com.reactnativenavigation.mocks.ImageLoaderMock;
42
 
43
 
43
             }
44
             }
44
         });
45
         });
45
-        uut.resolve(iconButton(), onSuccess);
46
+        uut.resolve(iconButton(), View.LAYOUT_DIRECTION_LTR, onSuccess);
46
         verify(imageLoader).loadIcon(eq(context), eq(ICON_URI), any());
47
         verify(imageLoader).loadIcon(eq(context), eq(ICON_URI), any());
47
         verify(onSuccess).run(any(Drawable.class));
48
         verify(onSuccess).run(any(Drawable.class));
48
     }
49
     }
55
 
56
 
56
             }
57
             }
57
         });
58
         });
58
-        uut.resolve(backButton(), onSuccess);
59
+        uut.resolve(backButton(), View.LAYOUT_DIRECTION_LTR, onSuccess);
59
         verifyZeroInteractions(imageLoader);
60
         verifyZeroInteractions(imageLoader);
60
         verify(onSuccess).run(any());
61
         verify(onSuccess).run(any());
61
     }
62
     }

+ 38
- 34
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java View File

34
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
34
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
35
 import com.reactnativenavigation.views.BottomTabs;
35
 import com.reactnativenavigation.views.BottomTabs;
36
 
36
 
37
+import com.facebook.react.ReactInstanceManager;
38
+
37
 import org.junit.Test;
39
 import org.junit.Test;
38
 import org.mockito.ArgumentCaptor;
40
 import org.mockito.ArgumentCaptor;
39
 import org.mockito.Mockito;
41
 import org.mockito.Mockito;
71
     private EventEmitter eventEmitter;
73
     private EventEmitter eventEmitter;
72
     private ViewController.ViewVisibilityListener parentVisibilityListener;
74
     private ViewController.ViewVisibilityListener parentVisibilityListener;
73
     private ModalStack modalStack;
75
     private ModalStack modalStack;
76
+    private ReactInstanceManager reactInstanceManager;
74
 
77
 
75
     @Override
78
     @Override
76
     public void beforeEach() {
79
     public void beforeEach() {
77
         childRegistry = new ChildControllersRegistry();
80
         childRegistry = new ChildControllersRegistry();
78
         eventEmitter = Mockito.mock(EventEmitter.class);
81
         eventEmitter = Mockito.mock(EventEmitter.class);
82
+        reactInstanceManager = Mockito.mock(ReactInstanceManager.class);
79
         overlayManager = spy(new OverlayManager());
83
         overlayManager = spy(new OverlayManager());
80
         imageLoaderMock = ImageLoaderMock.mock();
84
         imageLoaderMock = ImageLoaderMock.mock();
81
         activityController = newActivityController(TestActivity.class);
85
         activityController = newActivityController(TestActivity.class);
122
         uut.setDefaultOptions(new Options());
126
         uut.setDefaultOptions(new Options());
123
 
127
 
124
         SimpleViewController spy = spy(child1);
128
         SimpleViewController spy = spy(child1);
125
-        uut.setRoot(spy, new CommandListenerAdapter());
129
+        uut.setRoot(spy, new CommandListenerAdapter(), reactInstanceManager);
126
         Options defaultOptions = new Options();
130
         Options defaultOptions = new Options();
127
         uut.setDefaultOptions(defaultOptions);
131
         uut.setDefaultOptions(defaultOptions);
128
 
132
 
133
     @Test
137
     @Test
134
     public void setRoot_delegatesToRootPresenter() {
138
     public void setRoot_delegatesToRootPresenter() {
135
         CommandListenerAdapter listener = new CommandListenerAdapter();
139
         CommandListenerAdapter listener = new CommandListenerAdapter();
136
-        uut.setRoot(child1, listener);
140
+        uut.setRoot(child1, listener, reactInstanceManager);
137
         ArgumentCaptor<CommandListenerAdapter> captor = ArgumentCaptor.forClass(CommandListenerAdapter.class);
141
         ArgumentCaptor<CommandListenerAdapter> captor = ArgumentCaptor.forClass(CommandListenerAdapter.class);
138
-        verify(rootPresenter).setRoot(eq(child1), eq(uut.getDefaultOptions()), captor.capture());
142
+        verify(rootPresenter).setRoot(eq(child1), eq(uut.getDefaultOptions()), captor.capture(), eq(reactInstanceManager));
139
         assertThat(captor.getValue().getListener()).isEqualTo(listener);
143
         assertThat(captor.getValue().getListener()).isEqualTo(listener);
140
     }
144
     }
141
 
145
 
144
         FrameLayout content = activity.findViewById(android.R.id.content);
148
         FrameLayout content = activity.findViewById(android.R.id.content);
145
         assertThat(content.getChildCount()).isEqualTo(4); // 3 frame layouts and the default splash layout
149
         assertThat(content.getChildCount()).isEqualTo(4); // 3 frame layouts and the default splash layout
146
 
150
 
147
-        uut.setRoot(child2, new CommandListenerAdapter());
151
+        uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
148
 
152
 
149
         assertThat(content.getChildCount()).isEqualTo(3);
153
         assertThat(content.getChildCount()).isEqualTo(3);
150
     }
154
     }
151
 
155
 
152
     @Test
156
     @Test
153
     public void setRoot_AddsChildControllerView() {
157
     public void setRoot_AddsChildControllerView() {
154
-        uut.setRoot(child1, new CommandListenerAdapter());
158
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
155
         assertIsChild(uut.getRootLayout(), child1.getView());
159
         assertIsChild(uut.getRootLayout(), child1.getView());
156
     }
160
     }
157
 
161
 
158
     @Test
162
     @Test
159
     public void setRoot_ReplacesExistingChildControllerViews() {
163
     public void setRoot_ReplacesExistingChildControllerViews() {
160
-        uut.setRoot(child1, new CommandListenerAdapter());
161
-        uut.setRoot(child2, new CommandListenerAdapter());
164
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
165
+        uut.setRoot(child2, new CommandListenerAdapter(), reactInstanceManager);
162
         assertIsChild(uut.getRootLayout(), child2.getView());
166
         assertIsChild(uut.getRootLayout(), child2.getView());
163
     }
167
     }
164
 
168
 
172
     public void push() {
176
     public void push() {
173
         StackController stackController = newStack();
177
         StackController stackController = newStack();
174
         stackController.push(child1, new CommandListenerAdapter());
178
         stackController.push(child1, new CommandListenerAdapter());
175
-        uut.setRoot(stackController, new CommandListenerAdapter());
179
+        uut.setRoot(stackController, new CommandListenerAdapter(), reactInstanceManager);
176
 
180
 
177
         assertIsChild(uut.getView(), stackController.getView());
181
         assertIsChild(uut.getView(), stackController.getView());
178
         assertIsChild(stackController.getView(), child1.getView());
182
         assertIsChild(stackController.getView(), child1.getView());
185
 
189
 
186
     @Test
190
     @Test
187
     public void push_InvalidPushWithoutAStack_DoesNothing() {
191
     public void push_InvalidPushWithoutAStack_DoesNothing() {
188
-        uut.setRoot(child1, new CommandListenerAdapter());
192
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
189
         uut.push(child1.getId(), child2, new CommandListenerAdapter());
193
         uut.push(child1.getId(), child2, new CommandListenerAdapter());
190
         assertIsChild(uut.getView(), child1.getView());
194
         assertIsChild(uut.getView(), child1.getView());
191
     }
195
     }
197
         stack1.push(child1, new CommandListenerAdapter());
201
         stack1.push(child1, new CommandListenerAdapter());
198
         stack2.push(child2, new CommandListenerAdapter());
202
         stack2.push(child2, new CommandListenerAdapter());
199
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
203
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
200
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
204
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
201
 
205
 
202
         SimpleViewController newChild = new SimpleViewController(activity, childRegistry, "new child", tabOptions);
206
         SimpleViewController newChild = new SimpleViewController(activity, childRegistry, "new child", tabOptions);
203
         uut.push(child2.getId(), newChild, new CommandListenerAdapter());
207
         uut.push(child2.getId(), newChild, new CommandListenerAdapter());
216
     @Test
220
     @Test
217
     public void pop_InvalidDoesNothing() {
221
     public void pop_InvalidDoesNothing() {
218
         uut.pop("123", Options.EMPTY, new CommandListenerAdapter());
222
         uut.pop("123", Options.EMPTY, new CommandListenerAdapter());
219
-        uut.setRoot(child1, new CommandListenerAdapter());
223
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
220
         uut.pop(child1.getId(), Options.EMPTY, new CommandListenerAdapter());
224
         uut.pop(child1.getId(), Options.EMPTY, new CommandListenerAdapter());
221
         assertThat(uut.getChildControllers()).hasSize(1);
225
         assertThat(uut.getChildControllers()).hasSize(1);
222
     }
226
     }
226
         StackController stack1 = newStack();
230
         StackController stack1 = newStack();
227
         StackController stack2 = newStack();
231
         StackController stack2 = newStack();
228
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
232
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
229
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
233
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
230
         stack1.push(child1, new CommandListenerAdapter());
234
         stack1.push(child1, new CommandListenerAdapter());
231
         stack2.push(child2, new CommandListenerAdapter());
235
         stack2.push(child2, new CommandListenerAdapter());
232
         stack2.push(child3, new CommandListenerAdapter() {
236
         stack2.push(child3, new CommandListenerAdapter() {
249
         disablePushAnimation(child1, child2);
253
         disablePushAnimation(child1, child2);
250
         disablePopAnimation(child2, child1);
254
         disablePopAnimation(child2, child1);
251
         StackController stack = newStack(); stack.ensureViewIsCreated();
255
         StackController stack = newStack(); stack.ensureViewIsCreated();
252
-        uut.setRoot(stack, new CommandListenerAdapter());
256
+        uut.setRoot(stack, new CommandListenerAdapter(), reactInstanceManager);
253
         stack.push(child1, new CommandListenerAdapter());
257
         stack.push(child1, new CommandListenerAdapter());
254
         stack.push(child2, new CommandListenerAdapter());
258
         stack.push(child2, new CommandListenerAdapter());
255
 
259
 
262
         StackController stack1 = newStack();
266
         StackController stack1 = newStack();
263
         StackController stack2 = newStack();
267
         StackController stack2 = newStack();
264
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
268
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
265
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
269
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
266
 
270
 
267
         stack1.push(child1, new CommandListenerAdapter());
271
         stack1.push(child1, new CommandListenerAdapter());
268
         stack2.push(child2, new CommandListenerAdapter());
272
         stack2.push(child2, new CommandListenerAdapter());
282
         StackController stack1 = newStack();
286
         StackController stack1 = newStack();
283
         StackController stack2 = newStack();
287
         StackController stack2 = newStack();
284
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
288
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
285
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
289
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
286
 
290
 
287
         stack1.push(child1, new CommandListenerAdapter());
291
         stack1.push(child1, new CommandListenerAdapter());
288
         stack2.push(child2, new CommandListenerAdapter());
292
         stack2.push(child2, new CommandListenerAdapter());
302
         disablePushAnimation(child1, child2, child3);
306
         disablePushAnimation(child1, child2, child3);
303
 
307
 
304
         StackController stack = newStack();
308
         StackController stack = newStack();
305
-        uut.setRoot(stack, new CommandListenerAdapter());
309
+        uut.setRoot(stack, new CommandListenerAdapter(), reactInstanceManager);
306
 
310
 
307
         stack.push(child1, new CommandListenerAdapter());
311
         stack.push(child1, new CommandListenerAdapter());
308
         stack.push(child2, new CommandListenerAdapter());
312
         stack.push(child2, new CommandListenerAdapter());
316
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
320
         assertThat(uut.handleBack(new CommandListenerAdapter())).isFalse();
317
 
321
 
318
         ViewController root = spy(child1);
322
         ViewController root = spy(child1);
319
-        uut.setRoot(root, new CommandListenerAdapter());
323
+        uut.setRoot(root, new CommandListenerAdapter(), reactInstanceManager);
320
         when(root.handleBack(any(CommandListener.class))).thenReturn(true);
324
         when(root.handleBack(any(CommandListener.class))).thenReturn(true);
321
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
325
         assertThat(uut.handleBack(new CommandListenerAdapter())).isTrue();
322
         verify(root, times(1)).handleBack(any());
326
         verify(root, times(1)).handleBack(any());
325
     @Test
329
     @Test
326
     public void handleBack_modalTakePrecedenceOverRoot() {
330
     public void handleBack_modalTakePrecedenceOverRoot() {
327
         ViewController root = spy(child1);
331
         ViewController root = spy(child1);
328
-        uut.setRoot(root, new CommandListenerAdapter());
332
+        uut.setRoot(root, new CommandListenerAdapter(), reactInstanceManager);
329
         uut.showModal(child2, new CommandListenerAdapter());
333
         uut.showModal(child2, new CommandListenerAdapter());
330
         verify(root, times(0)).handleBack(new CommandListenerAdapter());
334
         verify(root, times(0)).handleBack(new CommandListenerAdapter());
331
     }
335
     }
335
         ComponentViewController componentVc = new SimpleComponentViewController(activity, childRegistry, "theId", new Options());
339
         ComponentViewController componentVc = new SimpleComponentViewController(activity, childRegistry, "theId", new Options());
336
         componentVc.setParentController(parentController);
340
         componentVc.setParentController(parentController);
337
         assertThat(componentVc.options.topBar.title.text.get("")).isEmpty();
341
         assertThat(componentVc.options.topBar.title.text.get("")).isEmpty();
338
-        uut.setRoot(componentVc, new CommandListenerAdapter());
342
+        uut.setRoot(componentVc, new CommandListenerAdapter(), reactInstanceManager);
339
 
343
 
340
         Options options = new Options();
344
         Options options = new Options();
341
         options.topBar.title.text = new Text("new title");
345
         options.topBar.title.text = new Text("new title");
368
 
372
 
369
     @Test
373
     @Test
370
     public void findController_root() {
374
     public void findController_root() {
371
-        uut.setRoot(child1, new CommandListenerAdapter());
375
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
372
         assertThat(uut.findController(child1.getId())).isEqualTo(child1);
376
         assertThat(uut.findController(child1.getId())).isEqualTo(child1);
373
     }
377
     }
374
 
378
 
399
     public void push_promise() {
403
     public void push_promise() {
400
         final StackController stackController = newStack();
404
         final StackController stackController = newStack();
401
         stackController.push(child1, new CommandListenerAdapter());
405
         stackController.push(child1, new CommandListenerAdapter());
402
-        uut.setRoot(stackController, new CommandListenerAdapter());
406
+        uut.setRoot(stackController, new CommandListenerAdapter(), reactInstanceManager);
403
 
407
 
404
         assertIsChild(uut.getView(), stackController.getView());
408
         assertIsChild(uut.getView(), stackController.getView());
405
         assertIsChild(stackController.getView(), child1.getView());
409
         assertIsChild(stackController.getView(), child1.getView());
415
 
419
 
416
     @Test
420
     @Test
417
     public void push_InvalidPushWithoutAStack_DoesNothing_Promise() {
421
     public void push_InvalidPushWithoutAStack_DoesNothing_Promise() {
418
-        uut.setRoot(child1, new CommandListenerAdapter());
422
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
419
         uut.push(child1.getId(), child2, new CommandListenerAdapter() {
423
         uut.push(child1.getId(), child2, new CommandListenerAdapter() {
420
             @Override
424
             @Override
421
             public void onError(String message) {
425
             public void onError(String message) {
428
     @Test
432
     @Test
429
     public void pop_InvalidDoesNothing_Promise() {
433
     public void pop_InvalidDoesNothing_Promise() {
430
         uut.pop("123", Options.EMPTY, new CommandListenerAdapter());
434
         uut.pop("123", Options.EMPTY, new CommandListenerAdapter());
431
-        uut.setRoot(child1, new CommandListenerAdapter());
435
+        uut.setRoot(child1, new CommandListenerAdapter(), reactInstanceManager);
432
         uut.pop(child1.getId(), Options.EMPTY, new CommandListenerAdapter() {
436
         uut.pop(child1.getId(), Options.EMPTY, new CommandListenerAdapter() {
433
             @Override
437
             @Override
434
             public void onError(String reason) {
438
             public void onError(String reason) {
442
         StackController stack1 = newStack();
446
         StackController stack1 = newStack();
443
         final StackController stack2 = newStack();
447
         final StackController stack2 = newStack();
444
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
448
         BottomTabsController bottomTabsController = newTabs(Arrays.asList(stack1, stack2));
445
-        uut.setRoot(bottomTabsController, new CommandListenerAdapter());
449
+        uut.setRoot(bottomTabsController, new CommandListenerAdapter(), reactInstanceManager);
446
 
450
 
447
         stack1.push(child1, new CommandListenerAdapter());
451
         stack1.push(child1, new CommandListenerAdapter());
448
         stack2.push(child2, new CommandListenerAdapter());
452
         stack2.push(child2, new CommandListenerAdapter());
458
 
462
 
459
     @Test
463
     @Test
460
     public void pushIntoModal() {
464
     public void pushIntoModal() {
461
-        uut.setRoot(parentController, new CommandListenerAdapter());
465
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
462
         StackController stackController = newStack();
466
         StackController stackController = newStack();
463
         stackController.push(child1, new CommandListenerAdapter());
467
         stackController.push(child1, new CommandListenerAdapter());
464
         uut.showModal(stackController, new CommandListenerAdapter());
468
         uut.showModal(stackController, new CommandListenerAdapter());
473
         StackController spy = spy(parentController);
477
         StackController spy = spy(parentController);
474
         StackController parent = newStack();
478
         StackController parent = newStack();
475
         parent.ensureViewIsCreated();
479
         parent.ensureViewIsCreated();
476
-        uut.setRoot(parent, new CommandListenerAdapter());
480
+        uut.setRoot(parent, new CommandListenerAdapter(), reactInstanceManager);
477
         parent.push(spy, new CommandListenerAdapter());
481
         parent.push(spy, new CommandListenerAdapter());
478
 
482
 
479
         spy.push(child1, new CommandListenerAdapter());
483
         spy.push(child1, new CommandListenerAdapter());
505
                     }
509
                     }
506
                 });
510
                 });
507
             }
511
             }
508
-        });
512
+        }, reactInstanceManager);
509
     }
513
     }
510
 
514
 
511
     @Test
515
     @Test
513
         disableShowModalAnimation(child1, child2, child3);
517
         disableShowModalAnimation(child1, child2, child3);
514
         disableDismissModalAnimation(child1, child2);
518
         disableDismissModalAnimation(child1, child2);
515
 
519
 
516
-        uut.setRoot(parentController, new CommandListenerAdapter());
520
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
517
         parentController.push(child3, new CommandListenerAdapter());
521
         parentController.push(child3, new CommandListenerAdapter());
518
         uut.showModal(child1, new CommandListenerAdapter());
522
         uut.showModal(child1, new CommandListenerAdapter());
519
         uut.showModal(child2, new CommandListenerAdapter());
523
         uut.showModal(child2, new CommandListenerAdapter());
532
     public void dismissModal_reattachedToRoot() {
536
     public void dismissModal_reattachedToRoot() {
533
         disableModalAnimations(child1);
537
         disableModalAnimations(child1);
534
 
538
 
535
-        uut.setRoot(parentController, new CommandListenerAdapter());
539
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
536
         assertThat(ViewUtils.isChildOf(uut.getRootLayout(), parentController.getView()));
540
         assertThat(ViewUtils.isChildOf(uut.getRootLayout(), parentController.getView()));
537
         uut.showModal(child1, new CommandListenerAdapter());
541
         uut.showModal(child1, new CommandListenerAdapter());
538
 
542
 
565
         uut.dismissAllModals(Options.EMPTY, new CommandListenerAdapter());
569
         uut.dismissAllModals(Options.EMPTY, new CommandListenerAdapter());
566
         verify(parentVisibilityListener, times(0)).onViewAppeared(parentController.getView());
570
         verify(parentVisibilityListener, times(0)).onViewAppeared(parentController.getView());
567
 
571
 
568
-        uut.setRoot(parentController, new CommandListenerAdapter());
572
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
569
         parentController.push(child2, new CommandListenerAdapter());
573
         parentController.push(child2, new CommandListenerAdapter());
570
 
574
 
571
         verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
575
         verify(parentVisibilityListener, times(1)).onViewAppeared(parentController.getView());
581
 
585
 
582
         parentController.push(child3, new CommandListenerAdapter());
586
         parentController.push(child3, new CommandListenerAdapter());
583
         StackController spy = spy(parentController);
587
         StackController spy = spy(parentController);
584
-        uut.setRoot(spy, new CommandListenerAdapter());
588
+        uut.setRoot(spy, new CommandListenerAdapter(), reactInstanceManager);
585
         uut.showModal(child1, new CommandListenerAdapter());
589
         uut.showModal(child1, new CommandListenerAdapter());
586
         uut.showModal(child2, new CommandListenerAdapter());
590
         uut.showModal(child2, new CommandListenerAdapter());
587
 
591
 
613
 
617
 
614
         StackController spy = spy(parentController);
618
         StackController spy = spy(parentController);
615
         spy.options.animations.setRoot.enabled = new Bool(false);
619
         spy.options.animations.setRoot.enabled = new Bool(false);
616
-        uut.setRoot(spy, new CommandListenerAdapter());
620
+        uut.setRoot(spy, new CommandListenerAdapter(), reactInstanceManager);
617
         spy.push(child1, new CommandListenerAdapter());
621
         spy.push(child1, new CommandListenerAdapter());
618
         activityController.destroy();
622
         activityController.destroy();
619
         verify(spy, times(1)).destroy();
623
         verify(spy, times(1)).destroy();
621
 
625
 
622
     @Test
626
     @Test
623
     public void destroy_destroyOverlayManager() {
627
     public void destroy_destroyOverlayManager() {
624
-        uut.setRoot(parentController, new CommandListenerAdapter());
628
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
625
         activityController.destroy();
629
         activityController.destroy();
626
         verify(overlayManager).destroy();
630
         verify(overlayManager).destroy();
627
     }
631
     }
628
 
632
 
629
     @Test
633
     @Test
630
     public void destroyViews() {
634
     public void destroyViews() {
631
-        uut.setRoot(parentController, new CommandListenerAdapter());
635
+        uut.setRoot(parentController, new CommandListenerAdapter(), reactInstanceManager);
632
         uut.showModal(child1, new CommandListenerAdapter());
636
         uut.showModal(child1, new CommandListenerAdapter());
633
         uut.showOverlay(child2, new CommandListenerAdapter());
637
         uut.showOverlay(child2, new CommandListenerAdapter());
634
         uut.destroy();
638
         uut.destroy();

+ 11
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.java View File

16
 import com.reactnativenavigation.viewcontrollers.ViewController;
16
 import com.reactnativenavigation.viewcontrollers.ViewController;
17
 import com.reactnativenavigation.views.element.ElementTransitionManager;
17
 import com.reactnativenavigation.views.element.ElementTransitionManager;
18
 
18
 
19
+import com.facebook.react.ReactInstanceManager;
20
+
19
 import org.junit.Test;
21
 import org.junit.Test;
20
 import org.mockito.ArgumentCaptor;
22
 import org.mockito.ArgumentCaptor;
23
+import org.mockito.Mockito;
21
 
24
 
22
 import static org.assertj.core.api.Java6Assertions.assertThat;
25
 import static org.assertj.core.api.Java6Assertions.assertThat;
23
 import static org.mockito.ArgumentMatchers.any;
26
 import static org.mockito.ArgumentMatchers.any;
34
     private ViewController root;
37
     private ViewController root;
35
     private NavigationAnimator animator;
38
     private NavigationAnimator animator;
36
     private Options defaultOptions;
39
     private Options defaultOptions;
40
+    private ReactInstanceManager reactInstanceManager;
37
 
41
 
38
 
42
 
39
     @Override
43
     @Override
40
     public void beforeEach() {
44
     public void beforeEach() {
45
+        reactInstanceManager = Mockito.mock(ReactInstanceManager.class);
41
         Activity activity = newActivity();
46
         Activity activity = newActivity();
42
         rootContainer = new FrameLayout(activity);
47
         rootContainer = new FrameLayout(activity);
43
         root = new SimpleViewController(activity, new ChildControllersRegistry(), "child1", new Options());
48
         root = new SimpleViewController(activity, new ChildControllersRegistry(), "child1", new Options());
49
 
54
 
50
     @Test
55
     @Test
51
     public void setRoot_viewIsAddedToContainer() {
56
     public void setRoot_viewIsAddedToContainer() {
52
-        uut.setRoot(root, defaultOptions, new CommandListenerAdapter());
57
+        uut.setRoot(root, defaultOptions, new CommandListenerAdapter(), reactInstanceManager);
53
         assertThat(root.getView().getParent()).isEqualTo(rootContainer);
58
         assertThat(root.getView().getParent()).isEqualTo(rootContainer);
54
     }
59
     }
55
 
60
 
56
     @Test
61
     @Test
57
     public void setRoot_reportsOnSuccess() {
62
     public void setRoot_reportsOnSuccess() {
58
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
63
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
59
-        uut.setRoot(root, defaultOptions, listener);
64
+        uut.setRoot(root, defaultOptions, listener, reactInstanceManager);
60
         verify(listener).onSuccess(root.getId());
65
         verify(listener).onSuccess(root.getId());
61
     }
66
     }
62
 
67
 
63
     @Test
68
     @Test
64
     public void setRoot_doesNotAnimateByDefault() {
69
     public void setRoot_doesNotAnimateByDefault() {
65
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
70
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
66
-        uut.setRoot(root, defaultOptions, listener);
71
+        uut.setRoot(root, defaultOptions, listener, reactInstanceManager);
67
         verifyZeroInteractions(animator);
72
         verifyZeroInteractions(animator);
68
         verify(listener).onSuccess(root.getId());
73
         verify(listener).onSuccess(root.getId());
69
     }
74
     }
82
         when(spy.resolveCurrentOptions(defaultOptions)).thenReturn(animatedSetRoot);
87
         when(spy.resolveCurrentOptions(defaultOptions)).thenReturn(animatedSetRoot);
83
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
88
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
84
 
89
 
85
-        uut.setRoot(spy, defaultOptions, listener);
90
+        uut.setRoot(spy, defaultOptions, listener, reactInstanceManager);
86
         verify(animator).setRoot(eq(spy.getView()), eq(animatedSetRoot.animations.setRoot), any());
91
         verify(animator).setRoot(eq(spy.getView()), eq(animatedSetRoot.animations.setRoot), any());
87
         verify(listener).onSuccess(spy.getId());
92
         verify(listener).onSuccess(spy.getId());
88
     }
93
     }
92
         root.options.animations.setRoot.waitForRender = new Bool(true);
97
         root.options.animations.setRoot.waitForRender = new Bool(true);
93
         ViewController spy = spy(root);
98
         ViewController spy = spy(root);
94
 
99
 
95
-        uut.setRoot(spy, defaultOptions, new CommandListenerAdapter());
100
+        uut.setRoot(spy, defaultOptions, new CommandListenerAdapter(), reactInstanceManager);
96
 
101
 
97
         ArgumentCaptor<Bool> captor = ArgumentCaptor.forClass(Bool.class);
102
         ArgumentCaptor<Bool> captor = ArgumentCaptor.forClass(Bool.class);
98
         verify(spy).setWaitForRender(captor.capture());
103
         verify(spy).setWaitForRender(captor.capture());
105
 
110
 
106
         ViewController spy = spy(root);
111
         ViewController spy = spy(root);
107
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
112
         CommandListenerAdapter listener = spy(new CommandListenerAdapter());
108
-        uut.setRoot(spy, defaultOptions, listener);
113
+        uut.setRoot(spy, defaultOptions, listener, reactInstanceManager);
109
         verify(spy).addOnAppearedListener(any());
114
         verify(spy).addOnAppearedListener(any());
110
         assertThat(spy.getView().getAlpha()).isZero();
115
         assertThat(spy.getView().getAlpha()).isZero();
111
         verifyZeroInteractions(listener);
116
         verifyZeroInteractions(listener);

+ 18
- 1
lib/ios/RNNCommandsHandler.m View File

7
 #import "RNNErrorHandler.h"
7
 #import "RNNErrorHandler.h"
8
 #import "RNNDefaultOptionsHelper.h"
8
 #import "RNNDefaultOptionsHelper.h"
9
 #import "UIViewController+RNNOptions.h"
9
 #import "UIViewController+RNNOptions.h"
10
+#import "React/RCTI18nUtil.h"
10
 
11
 
11
 static NSString* const setRoot	= @"setRoot";
12
 static NSString* const setRoot	= @"setRoot";
12
 static NSString* const setStackRoot	= @"setStackRoot";
13
 static NSString* const setStackRoot	= @"setStackRoot";
54
 
55
 
55
 - (void)setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion {
56
 - (void)setRoot:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion {
56
 	[self assertReady];
57
 	[self assertReady];
57
-	
58
+
59
+	if (@available(iOS 9, *)) {
60
+	    if(_controllerFactory.defaultOptions.layout.direction.hasValue) {
61
+	        if ([_controllerFactory.defaultOptions.layout.direction.get isEqualToString:@"rtl"]) {
62
+                [[RCTI18nUtil sharedInstance] allowRTL:YES];
63
+                [[RCTI18nUtil sharedInstance] forceRTL:YES];
64
+                [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
65
+                [[UINavigationBar appearance] setSemanticContentAttribute:UISemanticContentAttributeForceRightToLeft];
66
+            } else {
67
+                [[RCTI18nUtil sharedInstance] allowRTL:NO];
68
+                [[RCTI18nUtil sharedInstance] forceRTL:NO];
69
+                [[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
70
+                [[UINavigationBar appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
71
+            }
72
+	    }
73
+	}
74
+
58
 	[_modalManager dismissAllModalsAnimated:NO];
75
 	[_modalManager dismissAllModalsAnimated:NO];
59
 	[_store removeAllComponentsFromWindow:_mainWindow];
76
 	[_store removeAllComponentsFromWindow:_mainWindow];
60
 	
77
 	

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

3
 @interface RNNLayoutOptions : RNNOptions
3
 @interface RNNLayoutOptions : RNNOptions
4
 
4
 
5
 @property (nonatomic, strong) Color* backgroundColor;
5
 @property (nonatomic, strong) Color* backgroundColor;
6
+@property (nonatomic, strong) Text* direction;
6
 @property (nonatomic, strong) id orientation;
7
 @property (nonatomic, strong) id orientation;
7
 
8
 
8
 - (UIInterfaceOrientationMask)supportedOrientations;
9
 - (UIInterfaceOrientationMask)supportedOrientations;

+ 2
- 1
lib/ios/RNNLayoutOptions.m View File

8
 	self = [super init];
8
 	self = [super init];
9
 	
9
 	
10
 	self.backgroundColor = [ColorParser parse:dict key:@"backgroundColor"];
10
 	self.backgroundColor = [ColorParser parse:dict key:@"backgroundColor"];
11
+	self.direction = [TextParser parse:dict key:@"direction"];
11
 	self.orientation = dict[@"orientation"];
12
 	self.orientation = dict[@"orientation"];
12
-	
13
+
13
 	return self;
14
 	return self;
14
 }
15
 }
15
 
16
 

+ 9
- 0
lib/ios/RNNNavigationStackManager.m View File

1
 #import "RNNNavigationStackManager.h"
1
 #import "RNNNavigationStackManager.h"
2
 #import "RNNErrorHandler.h"
2
 #import "RNNErrorHandler.h"
3
+#import <React/RCTI18nUtil.h>
3
 
4
 
4
 typedef void (^RNNAnimationBlock)(void);
5
 typedef void (^RNNAnimationBlock)(void);
5
 
6
 
8
 - (void)push:(UIViewController *)newTop onTop:(UIViewController *)onTopViewController animated:(BOOL)animated animationDelegate:(id)animationDelegate completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
9
 - (void)push:(UIViewController *)newTop onTop:(UIViewController *)onTopViewController animated:(BOOL)animated animationDelegate:(id)animationDelegate completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
9
 	UINavigationController *nvc = onTopViewController.navigationController;
10
 	UINavigationController *nvc = onTopViewController.navigationController;
10
 
11
 
12
+	if([[RCTI18nUtil sharedInstance] isRTL]) {
13
+		nvc.view.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
14
+		nvc.navigationBar.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
15
+	} else {
16
+		nvc.view.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
17
+		nvc.navigationBar.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
18
+	}
19
+
11
 	if (animationDelegate) {
20
 	if (animationDelegate) {
12
 		nvc.delegate = animationDelegate;
21
 		nvc.delegate = animationDelegate;
13
 	} else {
22
 	} else {