Browse Source

Component type (#5820)

Send componentType field in componentDidAppear and componentDidDisappear events.

The new field is either:
- TopBarButton
- TopBarTitle
- TopBarBackground
- Component
Guy Carmeli 4 years ago
parent
commit
3878b683cc
No account linked to committer's email address
72 changed files with 549 additions and 292 deletions
  1. 24
    4
      e2e/StaticLifecycleEvents.test.js
  2. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  3. 1
    0
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  4. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java
  5. 1
    2
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactComponentViewCreator.java
  6. 8
    7
      lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java
  7. 18
    0
      lib/android/app/src/main/java/com/reactnativenavigation/react/events/ComponentType.java
  8. 6
    8
      lib/android/app/src/main/java/com/reactnativenavigation/react/events/EventEmitter.java
  9. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/utils/NativeCommandListener.java
  10. 0
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/IReactView.java
  11. 9
    7
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarButtonController.java
  12. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  13. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java
  14. 7
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java
  15. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/modal/ModalStack.java
  16. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/navigator/Navigator.java
  17. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java
  18. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java
  19. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java
  20. 6
    7
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentLayout.java
  21. 2
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/ComponentViewCreator.java
  22. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java
  23. 0
    10
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleOverlay.java
  24. 0
    10
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java
  25. 6
    6
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java
  26. 2
    2
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentViewCreator.java
  27. 17
    13
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestReactView.java
  28. 4
    3
      lib/android/app/src/test/java/com/reactnativenavigation/mocks/TopBarButtonCreatorMock.java
  29. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/utils/NativeCommandListenerTest.java
  30. 7
    5
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/ExternalComponentViewControllerTest.java
  31. 10
    8
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  32. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java
  33. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java
  34. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java
  35. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java
  36. 4
    0
      lib/ios/RNNBasePresenter.h
  37. 8
    0
      lib/ios/RNNBasePresenter.m
  38. 1
    1
      lib/ios/RNNBridgeManager.m
  39. 25
    3
      lib/ios/RNNComponentPresenter.m
  40. 9
    10
      lib/ios/RNNComponentViewController.m
  41. 11
    5
      lib/ios/RNNComponentViewCreator.h
  42. 2
    3
      lib/ios/RNNEventEmitter.h
  43. 6
    4
      lib/ios/RNNEventEmitter.m
  44. 4
    0
      lib/ios/RNNNavigationButtons.h
  45. 27
    1
      lib/ios/RNNNavigationButtons.m
  46. 5
    0
      lib/ios/RNNReactBackgroundView.h
  47. 9
    0
      lib/ios/RNNReactBackgroundView.m
  48. 5
    0
      lib/ios/RNNReactButtonView.h
  49. 9
    0
      lib/ios/RNNReactButtonView.m
  50. 3
    2
      lib/ios/RNNReactComponentRegistry.h
  51. 20
    12
      lib/ios/RNNReactComponentRegistry.m
  52. 2
    2
      lib/ios/RNNReactRootViewCreator.h
  53. 26
    17
      lib/ios/RNNReactRootViewCreator.m
  54. 9
    0
      lib/ios/RNNReactTitleView.h
  55. 44
    0
      lib/ios/RNNReactTitleView.m
  56. 15
    5
      lib/ios/RNNReactView.h
  57. 29
    35
      lib/ios/RNNReactView.m
  58. 15
    43
      lib/ios/RNNStackPresenter.m
  59. 0
    3
      lib/ios/RNNTopBarOptions.h
  60. 0
    1
      lib/ios/RNNTopBarOptions.m
  61. 3
    0
      lib/ios/RNNUIBarButtonItem.h
  62. 12
    0
      lib/ios/RNNUIBarButtonItem.m
  63. 24
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  64. 4
    0
      lib/ios/UIViewController+LayoutProtocol.h
  65. 11
    1
      lib/ios/UIViewController+LayoutProtocol.m
  66. 15
    14
      lib/src/events/ComponentEventsObserver.test.tsx
  67. 4
    0
      lib/src/interfaces/ComponentEvents.ts
  68. 6
    6
      playground/ios/NavigationTests/RNNComponentPresenterTest.m
  69. 1
    1
      playground/ios/NavigationTests/RNNTestRootViewCreator.m
  70. 9
    1
      playground/src/screens/PushedScreen.js
  71. 21
    1
      playground/src/screens/StaticLifecycleOverlay.js
  72. 1
    0
      playground/src/testIDs.js

+ 24
- 4
e2e/StaticLifecycleEvents.test.js View File

@@ -11,15 +11,15 @@ describe('static lifecycle events', () => {
11 11
   });
12 12
 
13 13
   it('didAppear didDisappear', async () => {
14
-    await expect(elementByLabel('componentDidAppear | EventsOverlay')).toBeVisible();
14
+    await expect(elementByLabel('componentDidAppear | EventsOverlay | Component')).toBeVisible();
15 15
     await elementById(TestIDs.PUSH_BTN).tap();
16
-    await expect(elementByLabel('componentDidAppear | Pushed')).toBeVisible();
17
-    await expect(elementByLabel('componentDidDisappear | EventsScreen')).toBeVisible();
16
+    await expect(elementByLabel('componentDidAppear | Pushed | Component')).toBeVisible();
17
+    await expect(elementByLabel('componentDidDisappear | EventsScreen | Component')).toBeVisible();
18 18
   });
19 19
 
20 20
   it('pushing and popping screen dispatch static event', async () => {
21 21
     await expect(elementByLabel('Static Lifecycle Events Overlay')).toBeVisible();
22
-    await expect(elementByLabel('componentDidAppear | EventsOverlay')).toBeVisible();
22
+    await expect(elementByLabel('componentDidAppear | EventsOverlay | Component')).toBeVisible();
23 23
     await elementById(TestIDs.PUSH_BTN).tap();
24 24
     await expect(elementByLabel('push')).toBeVisible();
25 25
     await elementById(TestIDs.POP_BTN).tap();
@@ -40,4 +40,24 @@ describe('static lifecycle events', () => {
40 40
     await expect(elementByLabel('Overlay Unmounted')).toBeVisible();
41 41
     await elementByLabel('OK').tap();
42 42
   });
43
+
44
+  it('top bar buttons didAppear didDisappear', async () => {
45
+    await elementById(TestIDs.PUSH_BTN).tap();
46
+    await elementById(TestIDs.PUSH_OPTIONS_BUTTON).tap();
47
+    await elementById(TestIDs.CLEAR_OVERLAY_EVENTS_BTN).tap();
48
+    await elementById(TestIDs.GOTO_BUTTONS_SCREEN).tap();
49
+    await expect(elementByLabel('componentDidAppear | CustomRoundedButton | TopBarButton')).toBeVisible();
50
+    await elementById(TestIDs.RESET_BUTTONS).tap();
51
+    await expect(elementByLabel('componentDidDisappear | CustomRoundedButton | TopBarButton')).toBeVisible();
52
+  });
53
+
54
+  it('top bar title didAppear didDisappear', async () => {
55
+    await elementById(TestIDs.PUSH_BTN).tap();
56
+    await elementById(TestIDs.PUSH_OPTIONS_BUTTON).tap();
57
+    await elementById(TestIDs.CLEAR_OVERLAY_EVENTS_BTN).tap();
58
+    await elementById(TestIDs.SET_REACT_TITLE_VIEW).tap();
59
+    await expect(elementByLabel('componentDidAppear | ReactTitleView | TopBarTitle')).toBeVisible();
60
+    await elementById(TestIDs.PUSH_BTN).tap();
61
+    await expect(elementByLabel('componentDidDisappear | ReactTitleView | TopBarTitle')).toBeVisible();
62
+  });
43 63
 });

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

@@ -11,7 +11,7 @@ import com.reactnativenavigation.presentation.Presenter;
11 11
 import com.reactnativenavigation.presentation.RenderChecker;
12 12
 import com.reactnativenavigation.presentation.SideMenuPresenter;
13 13
 import com.reactnativenavigation.presentation.StackPresenter;
14
-import com.reactnativenavigation.react.EventEmitter;
14
+import com.reactnativenavigation.react.events.EventEmitter;
15 15
 import com.reactnativenavigation.utils.Assertions;
16 16
 import com.reactnativenavigation.utils.ImageLoader;
17 17
 import com.reactnativenavigation.utils.TypefaceLoader;

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

@@ -1,5 +1,6 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
+import com.reactnativenavigation.react.events.EventEmitter;
3 4
 import com.reactnativenavigation.utils.LaunchArgsParser;
4 5
 import com.facebook.react.ReactInstanceManager;
5 6
 import com.facebook.react.bridge.Arguments;

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationReactInitializer.java View File

@@ -1,10 +1,11 @@
1 1
 package com.reactnativenavigation.react;
2 2
 
3
-import androidx.annotation.NonNull;
4
-
5 3
 import com.facebook.react.ReactInstanceManager;
6 4
 import com.facebook.react.bridge.ReactContext;
7 5
 import com.reactnativenavigation.NavigationActivity;
6
+import com.reactnativenavigation.react.events.EventEmitter;
7
+
8
+import androidx.annotation.NonNull;
8 9
 
9 10
 public class NavigationReactInitializer implements ReactInstanceManager.ReactInstanceEventListener {
10 11
 

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

@@ -4,7 +4,6 @@ import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6 6
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
7
-import com.reactnativenavigation.viewcontrollers.IReactView;
8 7
 
9 8
 public class ReactComponentViewCreator implements ReactViewCreator {
10 9
 	private ReactInstanceManager reactInstanceManager;
@@ -14,7 +13,7 @@ public class ReactComponentViewCreator implements ReactViewCreator {
14 13
 	}
15 14
 
16 15
 	@Override
17
-	public IReactView create(final Activity activity, final String componentId, final String componentName) {
16
+	public ReactView create(final Activity activity, final String componentId, final String componentName) {
18 17
 		return new ReactView(activity, reactInstanceManager, componentId, componentName);
19 18
 	}
20 19
 }

+ 8
- 7
lib/android/app/src/main/java/com/reactnativenavigation/react/ReactView.java View File

@@ -3,7 +3,6 @@ package com.reactnativenavigation.react;
3 3
 import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.os.Bundle;
6
-import androidx.annotation.RestrictTo;
7 6
 import android.view.MotionEvent;
8 7
 
9 8
 import com.facebook.react.ReactInstanceManager;
@@ -13,6 +12,8 @@ import com.facebook.react.uimanager.JSTouchDispatcher;
13 12
 import com.facebook.react.uimanager.UIManagerModule;
14 13
 import com.facebook.react.uimanager.events.EventDispatcher;
15 14
 import com.reactnativenavigation.interfaces.ScrollEventListener;
15
+import com.reactnativenavigation.react.events.ComponentType;
16
+import com.reactnativenavigation.react.events.EventEmitter;
16 17
 import com.reactnativenavigation.viewcontrollers.IReactView;
17 18
 import com.reactnativenavigation.views.Renderable;
18 19
 import com.reactnativenavigation.views.element.Element;
@@ -20,6 +21,8 @@ import com.reactnativenavigation.views.element.Element;
20 21
 import java.util.ArrayList;
21 22
 import java.util.List;
22 23
 
24
+import androidx.annotation.RestrictTo;
25
+
23 26
 @SuppressLint("ViewConstructor")
24 27
 public class ReactView extends ReactRootView implements IReactView, Renderable {
25 28
 
@@ -64,19 +67,17 @@ public class ReactView extends ReactRootView implements IReactView, Renderable {
64 67
 		unmountReactApplication();
65 68
 	}
66 69
 
67
-	@Override
68
-	public void sendComponentStart() {
70
+	public void sendComponentStart(ComponentType type) {
69 71
         ReactContext currentReactContext = reactInstanceManager.getCurrentReactContext();
70 72
         if (currentReactContext != null) {
71
-            new EventEmitter(currentReactContext).emitComponentDidAppear(componentId, componentName);
73
+            new EventEmitter(currentReactContext).emitComponentDidAppear(componentId, componentName, type);
72 74
         }
73 75
 	}
74 76
 
75
-	@Override
76
-	public void sendComponentStop() {
77
+	public void sendComponentStop(ComponentType type) {
77 78
         ReactContext currentReactContext = reactInstanceManager.getCurrentReactContext();
78 79
         if (currentReactContext != null) {
79
-            new EventEmitter(currentReactContext).emitComponentDidDisappear(componentId, componentName);
80
+            new EventEmitter(currentReactContext).emitComponentDidDisappear(componentId, componentName, type);
80 81
         }
81 82
 	}
82 83
 

+ 18
- 0
lib/android/app/src/main/java/com/reactnativenavigation/react/events/ComponentType.java View File

@@ -0,0 +1,18 @@
1
+package com.reactnativenavigation.react.events;
2
+
3
+public enum ComponentType {
4
+    Component("Component"),
5
+    Button("TopBarButton"),
6
+    Title("TopBarTitle"),
7
+    Background("TopBarBackground");
8
+
9
+    private String name;
10
+
11
+    public String getName() {
12
+        return name;
13
+    }
14
+
15
+    ComponentType(String name) {
16
+        this.name = name;
17
+    }
18
+    }

lib/android/app/src/main/java/com/reactnativenavigation/react/EventEmitter.java → lib/android/app/src/main/java/com/reactnativenavigation/react/events/EventEmitter.java View File

@@ -1,4 +1,4 @@
1
-package com.reactnativenavigation.react;
1
+package com.reactnativenavigation.react.events;
2 2
 
3 3
 import android.util.Log;
4 4
 
@@ -28,20 +28,22 @@ public class EventEmitter {
28 28
     }
29 29
 
30 30
     public void appLaunched() {
31
-        emit(AppLaunched);
31
+        emit(EventEmitter.AppLaunched, Arguments.createMap());
32 32
     }
33 33
 
34
-    public void emitComponentDidDisappear(String id, String componentName) {
34
+    public void emitComponentDidDisappear(String id, String componentName, ComponentType type) {
35 35
         WritableMap event = Arguments.createMap();
36 36
         event.putString("componentId", id);
37 37
         event.putString("componentName", componentName);
38
+        event.putString("componentType", type.getName());
38 39
         emit(ComponentDidDisappear, event);
39 40
     }
40 41
 
41
-    public void emitComponentDidAppear(String id, String componentName) {
42
+    public void emitComponentDidAppear(String id, String componentName, ComponentType type) {
42 43
         WritableMap event = Arguments.createMap();
43 44
         event.putString("componentId", id);
44 45
         event.putString("componentName", componentName);
46
+        event.putString("componentType", type.getName());
45 47
         emit(ComponentDidAppear, event);
46 48
     }
47 49
 
@@ -80,10 +82,6 @@ public class EventEmitter {
80 82
         emit(ScreenPopped, event);
81 83
     }
82 84
 
83
-    private void emit(String eventName) {
84
-        emit(eventName, Arguments.createMap());
85
-    }
86
-
87 85
     private void emit(String eventName, WritableMap data) {
88 86
         if (reactContext == null) {
89 87
             Log.e("RNN", "Could not send event " + eventName + ". React context is null!");

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

@@ -3,7 +3,7 @@ package com.reactnativenavigation.utils;
3 3
 import androidx.annotation.Nullable;
4 4
 
5 5
 import com.facebook.react.bridge.Promise;
6
-import com.reactnativenavigation.react.EventEmitter;
6
+import com.reactnativenavigation.react.events.EventEmitter;
7 7
 
8 8
 public class NativeCommandListener extends CommandListenerAdapter {
9 9
     private String commandId;

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

@@ -14,10 +14,6 @@ public interface IReactView extends Destroyable {
14 14
 
15 15
     View asView();
16 16
 
17
-    void sendComponentStart();
18
-
19
-    void sendComponentStop();
20
-
21 17
     void sendOnNavigationButtonPressed(String buttonId);
22 18
 
23 19
     ScrollEventListener getScrollEventListener();

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

@@ -1,13 +1,9 @@
1
-    package com.reactnativenavigation.viewcontrollers;
1
+package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.annotation.SuppressLint;
4 4
 import android.app.Activity;
5 5
 import android.graphics.Color;
6 6
 import android.graphics.drawable.Drawable;
7
-import androidx.annotation.NonNull;
8
-import androidx.annotation.RestrictTo;
9
-import androidx.appcompat.widget.ActionMenuView;
10
-import androidx.appcompat.widget.Toolbar;
11 7
 import android.view.Menu;
12 8
 import android.view.MenuItem;
13 9
 import android.widget.ImageButton;
@@ -16,6 +12,7 @@ import android.widget.TextView;
16 12
 import com.reactnativenavigation.parse.Options;
17 13
 import com.reactnativenavigation.parse.params.Button;
18 14
 import com.reactnativenavigation.parse.params.Text;
15
+import com.reactnativenavigation.react.events.ComponentType;
19 16
 import com.reactnativenavigation.utils.ArrayUtils;
20 17
 import com.reactnativenavigation.utils.ButtonPresenter;
21 18
 import com.reactnativenavigation.utils.ImageLoader;
@@ -28,6 +25,11 @@ import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
28 25
 import java.util.Collections;
29 26
 import java.util.List;
30 27
 
28
+import androidx.annotation.NonNull;
29
+import androidx.annotation.RestrictTo;
30
+import androidx.appcompat.widget.ActionMenuView;
31
+import androidx.appcompat.widget.Toolbar;
32
+
31 33
 public class TitleBarButtonController extends ViewController<TitleBarReactButtonView> implements MenuItem.OnMenuItemClickListener {
32 34
     public interface OnClickListener {
33 35
         void onPress(String buttonId);
@@ -69,13 +71,13 @@ public class TitleBarButtonController extends ViewController<TitleBarReactButton
69 71
     @SuppressLint("MissingSuperCall")
70 72
     @Override
71 73
     public void onViewAppeared() {
72
-        getView().sendComponentStart();
74
+        getView().sendComponentStart(ComponentType.Button);
73 75
     }
74 76
 
75 77
     @SuppressLint("MissingSuperCall")
76 78
     @Override
77 79
     public void onViewDisappear() {
78
-        getView().sendComponentStop();
80
+        getView().sendComponentStop(ComponentType.Button);
79 81
     }
80 82
 
81 83
     @Override

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

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.parse.Component;
6 6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.react.events.ComponentType;
7 8
 import com.reactnativenavigation.utils.CompatUtils;
8 9
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
9 10
 import com.reactnativenavigation.views.titlebar.TitleBarReactViewCreator;
@@ -23,13 +24,13 @@ public class TitleBarReactViewController extends ViewController<TitleBarReactVie
23 24
         super.onViewAppeared();
24 25
         if (!isDestroyed()) {
25 26
             runOnPreDraw(view -> view.setLayoutParams(view.getLayoutParams()));
26
-            getView().sendComponentStart();
27
+            getView().sendComponentStart(ComponentType.Title);
27 28
         }
28 29
     }
29 30
 
30 31
     @Override
31 32
     public void onViewDisappear() {
32
-        getView().sendComponentStop();
33
+        getView().sendComponentStop(ComponentType.Title);
33 34
         super.onViewDisappear();
34 35
     }
35 36
 

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

@@ -12,7 +12,7 @@ import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.presentation.BottomTabPresenter;
13 13
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
14 14
 import com.reactnativenavigation.presentation.Presenter;
15
-import com.reactnativenavigation.react.EventEmitter;
15
+import com.reactnativenavigation.react.events.EventEmitter;
16 16
 import com.reactnativenavigation.utils.CommandListener;
17 17
 import com.reactnativenavigation.utils.ImageLoader;
18 18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;

+ 7
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java View File

@@ -1,15 +1,14 @@
1 1
 package com.reactnativenavigation.viewcontrollers.externalcomponent;
2 2
 
3 3
 import android.app.Activity;
4
-import androidx.fragment.app.FragmentActivity;
5
-import androidx.core.view.ViewCompat;
6 4
 import android.view.View;
7 5
 
8 6
 import com.facebook.react.ReactInstanceManager;
9 7
 import com.reactnativenavigation.parse.ExternalComponent;
10 8
 import com.reactnativenavigation.parse.Options;
11 9
 import com.reactnativenavigation.presentation.ExternalComponentPresenter;
12
-import com.reactnativenavigation.react.EventEmitter;
10
+import com.reactnativenavigation.react.events.ComponentType;
11
+import com.reactnativenavigation.react.events.EventEmitter;
13 12
 import com.reactnativenavigation.utils.CoordinatorLayoutUtils;
14 13
 import com.reactnativenavigation.utils.StatusBarUtils;
15 14
 import com.reactnativenavigation.viewcontrollers.NoOpYellowBoxDelegate;
@@ -17,6 +16,9 @@ import com.reactnativenavigation.viewcontrollers.ViewController;
17 16
 import com.reactnativenavigation.views.BehaviourDelegate;
18 17
 import com.reactnativenavigation.views.ExternalComponentLayout;
19 18
 
19
+import androidx.core.view.ViewCompat;
20
+import androidx.fragment.app.FragmentActivity;
21
+
20 22
 import static com.reactnativenavigation.utils.ObjectUtils.perform;
21 23
 
22 24
 public class ExternalComponentViewController extends ViewController<ExternalComponentLayout> {
@@ -60,13 +62,13 @@ public class ExternalComponentViewController extends ViewController<ExternalComp
60 62
     @Override
61 63
     public void onViewAppeared() {
62 64
         super.onViewAppeared();
63
-        emitter.emitComponentDidAppear(getId(), externalComponent.name.get());
65
+        emitter.emitComponentDidAppear(getId(), externalComponent.name.get(), ComponentType.Component);
64 66
     }
65 67
 
66 68
     @Override
67 69
     public void onViewDisappear() {
68 70
         super.onViewDisappear();
69
-        emitter.emitComponentDidDisappear(getId(), externalComponent.name.get());
71
+        emitter.emitComponentDidDisappear(getId(), externalComponent.name.get(), ComponentType.Component);
70 72
     }
71 73
 
72 74
     @Override

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

@@ -7,7 +7,7 @@ import android.view.ViewGroup;
7 7
 
8 8
 import com.reactnativenavigation.anim.ModalAnimator;
9 9
 import com.reactnativenavigation.parse.Options;
10
-import com.reactnativenavigation.react.EventEmitter;
10
+import com.reactnativenavigation.react.events.EventEmitter;
11 11
 import com.reactnativenavigation.utils.CommandListener;
12 12
 import com.reactnativenavigation.utils.CommandListenerAdapter;
13 13
 import com.reactnativenavigation.viewcontrollers.ViewController;

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

@@ -8,7 +8,7 @@ import com.reactnativenavigation.parse.Options;
8 8
 import com.reactnativenavigation.presentation.OverlayManager;
9 9
 import com.reactnativenavigation.presentation.Presenter;
10 10
 import com.reactnativenavigation.presentation.RootPresenter;
11
-import com.reactnativenavigation.react.EventEmitter;
11
+import com.reactnativenavigation.react.events.EventEmitter;
12 12
 import com.reactnativenavigation.utils.CommandListener;
13 13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14 14
 import com.reactnativenavigation.utils.CompatUtils;

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

@@ -9,7 +9,7 @@ import com.reactnativenavigation.parse.Options;
9 9
 import com.reactnativenavigation.presentation.Presenter;
10 10
 import com.reactnativenavigation.presentation.StackPresenter;
11 11
 import com.reactnativenavigation.react.Constants;
12
-import com.reactnativenavigation.react.EventEmitter;
12
+import com.reactnativenavigation.react.events.EventEmitter;
13 13
 import com.reactnativenavigation.utils.CommandListener;
14 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
15 15
 import com.reactnativenavigation.utils.CompatUtils;

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

@@ -6,7 +6,7 @@ import com.reactnativenavigation.anim.NavigationAnimator;
6 6
 import com.reactnativenavigation.parse.Options;
7 7
 import com.reactnativenavigation.presentation.Presenter;
8 8
 import com.reactnativenavigation.presentation.StackPresenter;
9
-import com.reactnativenavigation.react.EventEmitter;
9
+import com.reactnativenavigation.react.events.EventEmitter;
10 10
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
11 11
 import com.reactnativenavigation.viewcontrollers.ViewController;
12 12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;

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

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 
5 5
 import com.reactnativenavigation.parse.Component;
6 6
 import com.reactnativenavigation.parse.Options;
7
+import com.reactnativenavigation.react.events.ComponentType;
7 8
 import com.reactnativenavigation.utils.CompatUtils;
8 9
 import com.reactnativenavigation.viewcontrollers.ViewController;
9 10
 import com.reactnativenavigation.viewcontrollers.YellowBoxDelegate;
@@ -28,12 +29,12 @@ public class TopBarBackgroundViewController extends ViewController<TopBarBackgro
28 29
     @Override
29 30
     public void onViewAppeared() {
30 31
         super.onViewAppeared();
31
-        getView().sendComponentStart();
32
+        getView().sendComponentStart(ComponentType.Background);
32 33
     }
33 34
 
34 35
     @Override
35 36
     public void onViewDisappear() {
36
-        getView().sendComponentStop();
37
+        getView().sendComponentStop(ComponentType.Background);
37 38
         super.onViewDisappear();
38 39
     }
39 40
 

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

@@ -8,7 +8,8 @@ import android.view.View;
8 8
 import com.reactnativenavigation.interfaces.ScrollEventListener;
9 9
 import com.reactnativenavigation.parse.Options;
10 10
 import com.reactnativenavigation.parse.params.Bool;
11
-import com.reactnativenavigation.viewcontrollers.IReactView;
11
+import com.reactnativenavigation.react.ReactView;
12
+import com.reactnativenavigation.react.events.ComponentType;
12 13
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
13 14
 import com.reactnativenavigation.views.element.Element;
14 15
 import com.reactnativenavigation.views.touch.OverlayTouchDelegate;
@@ -22,10 +23,10 @@ import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParent
22 23
 @SuppressLint("ViewConstructor")
23 24
 public class ComponentLayout extends CoordinatorLayout implements ReactComponent, TitleBarButtonController.OnClickListener {
24 25
 
25
-    private IReactView reactView;
26
+    private ReactView reactView;
26 27
     private final OverlayTouchDelegate touchDelegate;
27 28
 
28
-    public ComponentLayout(Context context, IReactView reactView) {
29
+    public ComponentLayout(Context context, ReactView reactView) {
29 30
 		super(context);
30 31
 		this.reactView = reactView;
31 32
         addView(reactView.asView(), matchParentLP());
@@ -47,14 +48,12 @@ public class ComponentLayout extends CoordinatorLayout implements ReactComponent
47 48
         reactView.destroy();
48 49
     }
49 50
 
50
-	@Override
51 51
 	public void sendComponentStart() {
52
-		reactView.sendComponentStart();
52
+		reactView.sendComponentStart(ComponentType.Component);
53 53
 	}
54 54
 
55
-	@Override
56 55
 	public void sendComponentStop() {
57
-		reactView.sendComponentStop();
56
+		reactView.sendComponentStop(ComponentType.Component);
58 57
 	}
59 58
 
60 59
     public void applyOptions(Options options) {

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

@@ -4,6 +4,7 @@ import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6 6
 import com.reactnativenavigation.react.ReactComponentViewCreator;
7
+import com.reactnativenavigation.react.ReactView;
7 8
 import com.reactnativenavigation.viewcontrollers.IReactView;
8 9
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
9 10
 
@@ -17,7 +18,7 @@ public class ComponentViewCreator implements ReactViewCreator {
17 18
 
18 19
 	@Override
19 20
 	public IReactView create(Activity activity, String componentId, String componentName) {
20
-        IReactView reactView = new ReactComponentViewCreator(instanceManager).create(activity, componentId, componentName);
21
+        ReactView reactView = new ReactComponentViewCreator(instanceManager).create(activity, componentId, componentName);
21 22
         return new ComponentLayout(activity, reactView);
22 23
 	}
23 24
 }

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

@@ -12,7 +12,7 @@ import com.reactnativenavigation.parse.Options;
12 12
 import com.reactnativenavigation.parse.params.Bool;
13 13
 import com.reactnativenavigation.presentation.RenderChecker;
14 14
 import com.reactnativenavigation.presentation.StackPresenter;
15
-import com.reactnativenavigation.react.EventEmitter;
15
+import com.reactnativenavigation.react.events.EventEmitter;
16 16
 import com.reactnativenavigation.utils.ImageLoader;
17 17
 import com.reactnativenavigation.utils.UiUtils;
18 18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;

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

@@ -36,16 +36,6 @@ public class SimpleOverlay extends RelativeLayout implements IReactView {
36 36
 
37 37
     }
38 38
 
39
-    @Override
40
-    public void sendComponentStart() {
41
-
42
-    }
43
-
44
-    @Override
45
-    public void sendComponentStop() {
46
-
47
-    }
48
-
49 39
     @Override
50 40
     public void sendOnNavigationButtonPressed(String buttonId) {
51 41
 

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

@@ -90,16 +90,6 @@ public class SimpleViewController extends ChildController<SimpleViewController.S
90 90
 
91 91
         }
92 92
 
93
-        @Override
94
-        public void sendComponentStart() {
95
-
96
-        }
97
-
98
-        @Override
99
-        public void sendComponentStop() {
100
-
101
-        }
102
-
103 93
         @Override
104 94
         public void sendOnNavigationButtonPressed(String buttonId) {
105 95
 

+ 6
- 6
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentLayout.java View File

@@ -6,20 +6,20 @@ import android.view.View;
6 6
 
7 7
 import com.reactnativenavigation.interfaces.ScrollEventListener;
8 8
 import com.reactnativenavigation.parse.Options;
9
-import com.reactnativenavigation.viewcontrollers.IReactView;
9
+import com.reactnativenavigation.react.ReactView;
10
+import com.reactnativenavigation.react.events.ComponentType;
10 11
 import com.reactnativenavigation.viewcontrollers.TitleBarButtonController;
11 12
 import com.reactnativenavigation.views.ComponentLayout;
12 13
 
13 14
 public class TestComponentLayout extends ComponentLayout implements TitleBarButtonController.OnClickListener {
14 15
 
15
-    private IReactView reactView;
16
+    private ReactView reactView;
16 17
 
17
-    public TestComponentLayout(final Context context, IReactView reactView) {
18
+    public TestComponentLayout(final Context context, ReactView reactView) {
18 19
         super(context, reactView);
19 20
         this.reactView = reactView;
20 21
     }
21 22
 
22
-
23 23
     @Override
24 24
     public boolean isReady() {
25 25
         return false;
@@ -36,12 +36,12 @@ public class TestComponentLayout extends ComponentLayout implements TitleBarButt
36 36
 
37 37
     @Override
38 38
     public void sendComponentStart() {
39
-        reactView.sendComponentStart();
39
+        reactView.sendComponentStart(ComponentType.Component);
40 40
     }
41 41
 
42 42
     @Override
43 43
     public void sendComponentStop() {
44
-        reactView.sendComponentStop();
44
+        reactView.sendComponentStop(ComponentType.Component);
45 45
     }
46 46
 
47 47
     @Override

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestComponentViewCreator.java View File

@@ -2,8 +2,8 @@ package com.reactnativenavigation.mocks;
2 2
 
3 3
 import android.app.Activity;
4 4
 
5
+import com.reactnativenavigation.react.ReactView;
5 6
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
6
-import com.reactnativenavigation.viewcontrollers.IReactView;
7 7
 import com.reactnativenavigation.views.ComponentLayout;
8 8
 import com.reactnativenavigation.views.ReactComponent;
9 9
 
@@ -12,7 +12,7 @@ import static org.mockito.Mockito.spy;
12 12
 public class TestComponentViewCreator implements ReactViewCreator {
13 13
     @Override
14 14
     public ReactComponent create(final Activity activity, final String componentId, final String componentName) {
15
-        IReactView reactView = spy(new TestReactView(activity));
15
+        ReactView reactView = spy(new TestReactView(activity));
16 16
         return new ComponentLayout(activity, reactView);
17 17
     }
18 18
 }

+ 17
- 13
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestReactView.java View File

@@ -1,46 +1,50 @@
1 1
 package com.reactnativenavigation.mocks;
2 2
 
3 3
 import android.content.Context;
4
-import androidx.annotation.NonNull;
4
+import android.os.Bundle;
5 5
 import android.view.MotionEvent;
6
-import android.view.View;
7
-import android.widget.FrameLayout;
8 6
 
7
+import com.facebook.react.ReactInstanceManager;
9 8
 import com.reactnativenavigation.interfaces.ScrollEventListener;
9
+import com.reactnativenavigation.react.ReactView;
10
+import com.reactnativenavigation.react.events.ComponentType;
10 11
 import com.reactnativenavigation.viewcontrollers.IReactView;
11 12
 import com.reactnativenavigation.views.element.Element;
12 13
 
13 14
 import java.util.Collections;
14 15
 import java.util.List;
15 16
 
16
-public class TestReactView extends FrameLayout implements IReactView {
17
+import androidx.annotation.NonNull;
18
+import androidx.annotation.Nullable;
19
+
20
+public class TestReactView extends ReactView implements IReactView {
17 21
 
18 22
     public TestReactView(@NonNull Context context) {
19
-        super(context);
23
+        super(context, null, "", "");
20 24
     }
21 25
 
22 26
     @Override
23
-    public boolean isReady() {
24
-        return false;
27
+    public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties, @Nullable String initialUITemplate) {
28
+
25 29
     }
26 30
 
27 31
     @Override
28
-    public View asView() {
29
-        return this;
32
+    public void sendComponentStart(ComponentType type) {
33
+
30 34
     }
31 35
 
32 36
     @Override
33
-    public void destroy() {
37
+    public void sendComponentStop(ComponentType type) {
34 38
 
35 39
     }
36 40
 
37 41
     @Override
38
-    public void sendComponentStart() {
39
-
42
+    public boolean isReady() {
43
+        return false;
40 44
     }
41 45
 
42 46
     @Override
43
-    public void sendComponentStop() {
47
+    public void destroy() {
44 48
 
45 49
     }
46 50
 

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

@@ -3,8 +3,9 @@ package com.reactnativenavigation.mocks;
3 3
 import android.app.Activity;
4 4
 
5 5
 import com.facebook.react.ReactInstanceManager;
6
-import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
6
+import com.reactnativenavigation.react.events.ComponentType;
7 7
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
8
+import com.reactnativenavigation.views.titlebar.TitleBarReactButtonView;
8 9
 
9 10
 import static org.mockito.Mockito.mock;
10 11
 
@@ -15,12 +16,12 @@ public class TopBarButtonCreatorMock implements ReactViewCreator {
15 16
         final ReactInstanceManager reactInstanceManager = mock(ReactInstanceManager.class);
16 17
         return new TitleBarReactButtonView(activity, reactInstanceManager, componentId, componentName) {
17 18
             @Override
18
-            public void sendComponentStart() {
19
+            public void sendComponentStart(ComponentType type) {
19 20
 
20 21
             }
21 22
 
22 23
             @Override
23
-            public void sendComponentStop() {
24
+            public void sendComponentStop(ComponentType type) {
24 25
 
25 26
             }
26 27
         };

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/utils/NativeCommandListenerTest.java View File

@@ -2,7 +2,7 @@ package com.reactnativenavigation.utils;
2 2
 
3 3
 import com.facebook.react.bridge.Promise;
4 4
 import com.reactnativenavigation.BaseTest;
5
-import com.reactnativenavigation.react.EventEmitter;
5
+import com.reactnativenavigation.react.events.EventEmitter;
6 6
 
7 7
 import org.junit.Test;
8 8
 import org.mockito.ArgumentCaptor;

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

@@ -1,8 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
5
-import androidx.fragment.app.FragmentActivity;
6 4
 
7 5
 import com.facebook.react.ReactInstanceManager;
8 6
 import com.reactnativenavigation.BaseTest;
@@ -10,7 +8,8 @@ import com.reactnativenavigation.parse.ExternalComponent;
10 8
 import com.reactnativenavigation.parse.Options;
11 9
 import com.reactnativenavigation.parse.params.Text;
12 10
 import com.reactnativenavigation.presentation.ExternalComponentPresenter;
13
-import com.reactnativenavigation.react.EventEmitter;
11
+import com.reactnativenavigation.react.events.ComponentType;
12
+import com.reactnativenavigation.react.events.EventEmitter;
14 13
 import com.reactnativenavigation.viewcontrollers.externalcomponent.ExternalComponentViewController;
15 14
 import com.reactnativenavigation.viewcontrollers.externalcomponent.FragmentCreatorMock;
16 15
 import com.reactnativenavigation.views.ExternalComponentLayout;
@@ -19,6 +18,9 @@ import org.json.JSONObject;
19 18
 import org.junit.Test;
20 19
 import org.mockito.Mockito;
21 20
 
21
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
22
+import androidx.fragment.app.FragmentActivity;
23
+
22 24
 import static org.assertj.core.api.Java6Assertions.assertThat;
23 25
 import static org.mockito.Mockito.spy;
24 26
 import static org.mockito.Mockito.times;
@@ -66,13 +68,13 @@ public class ExternalComponentViewControllerTest extends BaseTest {
66 68
     @Test
67 69
     public void onViewAppeared_appearEventIsEmitted() {
68 70
         uut.onViewAppeared();
69
-        verify(emitter).emitComponentDidAppear(uut.getId(), ec.name.get());
71
+        verify(emitter).emitComponentDidAppear(uut.getId(), ec.name.get(), ComponentType.Component);
70 72
     }
71 73
 
72 74
     @Test
73 75
     public void onViewDisappear_disappearEventIsEmitted() {
74 76
         uut.onViewDisappear();
75
-        verify(emitter).emitComponentDidDisappear(uut.getId(), ec.name.get());
77
+        verify(emitter).emitComponentDidDisappear(uut.getId(), ec.name.get(), ComponentType.Component);
76 78
     }
77 79
 
78 80
     private ExternalComponent createExternalComponent() {

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

@@ -1,7 +1,6 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3 3
 import android.app.Activity;
4
-import androidx.annotation.NonNull;
5 4
 import android.view.ViewGroup;
6 5
 
7 6
 import com.reactnativenavigation.BaseTest;
@@ -14,6 +13,7 @@ import com.reactnativenavigation.parse.params.Bool;
14 13
 import com.reactnativenavigation.parse.params.Text;
15 14
 import com.reactnativenavigation.presentation.ComponentPresenter;
16 15
 import com.reactnativenavigation.presentation.Presenter;
16
+import com.reactnativenavigation.react.events.ComponentType;
17 17
 import com.reactnativenavigation.utils.CommandListenerAdapter;
18 18
 import com.reactnativenavigation.utils.ViewHelper;
19 19
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
@@ -28,6 +28,8 @@ import org.mockito.Mockito;
28 28
 import java.util.ArrayList;
29 29
 import java.util.List;
30 30
 
31
+import androidx.annotation.NonNull;
32
+
31 33
 import static org.assertj.core.api.Java6Assertions.assertThat;
32 34
 import static org.mockito.ArgumentMatchers.any;
33 35
 import static org.mockito.ArgumentMatchers.eq;
@@ -133,9 +135,9 @@ public class TopTabsViewControllerTest extends BaseTest {
133 135
         TestReactView selectedTab = getActualTabView(1);
134 136
 
135 137
         uut.switchToTab(1);
136
-        verify(initialTab, times(1)).sendComponentStop();
137
-        verify(selectedTab, times(1)).sendComponentStart();
138
-        verify(selectedTab, times(0)).sendComponentStop();
138
+        verify(initialTab, times(1)).sendComponentStop(ComponentType.Component);
139
+        verify(selectedTab, times(1)).sendComponentStart(ComponentType.Component);
140
+        verify(selectedTab, times(0)).sendComponentStop(ComponentType.Component);
139 141
     }
140 142
 
141 143
     @Test
@@ -146,10 +148,10 @@ public class TopTabsViewControllerTest extends BaseTest {
146 148
         uut.switchToTab(1);
147 149
         uut.switchToTab(0);
148 150
 
149
-        verify(getActualTabView(0), times(1)).sendComponentStop();
150
-        verify(getActualTabView(0), times(2)).sendComponentStart();
151
-        verify(getActualTabView(1), times(1)).sendComponentStart();
152
-        verify(getActualTabView(1), times(1)).sendComponentStop();
151
+        verify(getActualTabView(0), times(1)).sendComponentStop(ComponentType.Component);
152
+        verify(getActualTabView(0), times(2)).sendComponentStart(ComponentType.Component);
153
+        verify(getActualTabView(1), times(1)).sendComponentStart(ComponentType.Component);
154
+        verify(getActualTabView(1), times(1)).sendComponentStop(ComponentType.Component);
153 155
     }
154 156
 
155 157
     @Test

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

@@ -18,7 +18,7 @@ import com.reactnativenavigation.parse.params.Text;
18 18
 import com.reactnativenavigation.presentation.BottomTabPresenter;
19 19
 import com.reactnativenavigation.presentation.BottomTabsPresenter;
20 20
 import com.reactnativenavigation.presentation.Presenter;
21
-import com.reactnativenavigation.react.EventEmitter;
21
+import com.reactnativenavigation.react.events.EventEmitter;
22 22
 import com.reactnativenavigation.utils.CommandListenerAdapter;
23 23
 import com.reactnativenavigation.utils.ImageLoader;
24 24
 import com.reactnativenavigation.utils.OptionHelper;

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

@@ -9,7 +9,7 @@ import com.reactnativenavigation.*;
9 9
 import com.reactnativenavigation.anim.ModalAnimator;
10 10
 import com.reactnativenavigation.mocks.SimpleViewController;
11 11
 import com.reactnativenavigation.parse.Options;
12
-import com.reactnativenavigation.react.EventEmitter;
12
+import com.reactnativenavigation.react.events.EventEmitter;
13 13
 import com.reactnativenavigation.utils.CommandListener;
14 14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
15 15
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;

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

@@ -19,7 +19,7 @@ import com.reactnativenavigation.presentation.BottomTabsPresenter;
19 19
 import com.reactnativenavigation.presentation.OverlayManager;
20 20
 import com.reactnativenavigation.presentation.Presenter;
21 21
 import com.reactnativenavigation.presentation.RootPresenter;
22
-import com.reactnativenavigation.react.EventEmitter;
22
+import com.reactnativenavigation.react.events.EventEmitter;
23 23
 import com.reactnativenavigation.utils.CommandListener;
24 24
 import com.reactnativenavigation.utils.CommandListenerAdapter;
25 25
 import com.reactnativenavigation.utils.CompatUtils;

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

@@ -22,7 +22,7 @@ import com.reactnativenavigation.parse.params.Bool;
22 22
 import com.reactnativenavigation.parse.params.Text;
23 23
 import com.reactnativenavigation.presentation.RenderChecker;
24 24
 import com.reactnativenavigation.presentation.StackPresenter;
25
-import com.reactnativenavigation.react.EventEmitter;
25
+import com.reactnativenavigation.react.events.EventEmitter;
26 26
 import com.reactnativenavigation.utils.CommandListenerAdapter;
27 27
 import com.reactnativenavigation.utils.StatusBarUtils;
28 28
 import com.reactnativenavigation.utils.TitleBarHelper;

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

@@ -30,6 +30,10 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
30 30
 
31 31
 - (void)viewDidLayoutSubviews;
32 32
 
33
+- (void)componentDidAppear;
34
+
35
+- (void)componentDidDisappear;
36
+
33 37
 - (UIStatusBarStyle)getStatusBarStyle:(RNNNavigationOptions *)resolvedOptions;
34 38
 
35 39
 - (UIInterfaceOrientationMask)getOrientation:(RNNNavigationOptions *)options;

+ 8
- 0
lib/ios/RNNBasePresenter.m View File

@@ -28,6 +28,14 @@
28 28
     _defaultOptions = defaultOptions;
29 29
 }
30 30
 
31
+- (void)componentDidAppear {
32
+    
33
+}
34
+
35
+- (void)componentDidDisappear {
36
+    
37
+}
38
+
31 39
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)initialOptions {
32 40
     UIViewController* viewController = self.boundViewController;
33 41
     RNNNavigationOptions *withDefault = [initialOptions withDefault:[self defaultOptions]];

+ 1
- 1
lib/ios/RNNBridgeManager.m View File

@@ -87,7 +87,7 @@
87 87
 - (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
88 88
 	RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init];
89 89
 
90
-	id<RNNComponentViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
90
+	id<RNNComponentViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge eventEmitter:eventEmitter];
91 91
 	_componentRegistry = [[RNNReactComponentRegistry alloc] initWithCreator:rootViewCreator];
92 92
 	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter store:_store componentRegistry:_componentRegistry andBridge:bridge bottomTabsAttachModeFactory:[BottomTabsAttachModeFactory new]];
93 93
 

+ 25
- 3
lib/ios/RNNComponentPresenter.m View File

@@ -4,9 +4,10 @@
4 4
 #import "RCTConvert+Modal.h"
5 5
 #import "RNNTitleViewHelper.h"
6 6
 #import "UIViewController+LayoutProtocol.h"
7
+#import "RNNReactTitleView.h"
7 8
 
8 9
 @interface RNNComponentPresenter() {
9
-	RNNReactView* _customTitleView;
10
+	RNNReactTitleView* _customTitleView;
10 11
 	RNNTitleViewHelper* _titleViewHelper;
11 12
 	RNNReactComponentRegistry* _componentRegistry;
12 13
 }
@@ -26,6 +27,25 @@
26 27
 	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.boundViewController componentRegistry:_componentRegistry];
27 28
 }
28 29
 
30
+- (void)componentDidAppear {
31
+    RNNReactView* component = (RNNReactView *)self.boundViewController.view;
32
+    if ([component respondsToSelector:@selector(componentDidAppear)]) {
33
+        [component componentDidAppear];
34
+    }
35
+    [_customTitleView componentDidAppear];
36
+    [_navigationButtons componentDidAppear];
37
+}
38
+
39
+- (void)componentDidDisappear {
40
+    RNNReactView* component = (RNNReactView *)self.boundViewController.view;
41
+    if ([component respondsToSelector:@selector(componentDidDisappear)]) {
42
+        [component componentDidDisappear];
43
+    }
44
+    
45
+    [_customTitleView componentDidDisappear];
46
+    [_navigationButtons componentDidDisappear];
47
+}
48
+
29 49
 - (void)applyOptionsOnWillMoveToParentViewController:(RNNNavigationOptions *)options {
30 50
 	[super applyOptionsOnWillMoveToParentViewController:options];
31 51
 }
@@ -156,7 +176,8 @@
156 176
 }
157 177
 
158 178
 - (void)removeTitleComponentIfNeeded:(RNNNavigationOptions *)options {
159
-	if (options.topBar.title.text.hasValue && !options.topBar.component.hasValue) {
179
+	if (options.topBar.title.text.hasValue) {
180
+        [_customTitleView componentDidDisappear];
160 181
 		[_customTitleView removeFromSuperview];
161 182
 		_customTitleView = nil;
162 183
 	}
@@ -175,7 +196,7 @@
175 196
 	}
176 197
 	
177 198
 	if (options.topBar.title.component.name.hasValue) {
178
-		_customTitleView = [_componentRegistry createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
199
+        _customTitleView = (RNNReactTitleView *)[_componentRegistry createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId componentType:RNNComponentTypeTopBarTitle reactViewReadyBlock:readyBlock];
179 200
 		_customTitleView.backgroundColor = UIColor.clearColor;
180 201
 		NSString* alignment = [options.topBar.title.component.alignment getWithDefaultValue:@""];
181 202
 		[_customTitleView setAlignment:alignment inFrame:viewController.navigationController.navigationBar.frame];
@@ -183,6 +204,7 @@
183 204
 		
184 205
 		viewController.navigationItem.titleView = nil;
185 206
 		viewController.navigationItem.titleView = _customTitleView;
207
+        [_customTitleView componentDidAppear];
186 208
 	} else {
187 209
 		[_customTitleView removeFromSuperview];
188 210
 		if (readyBlock) {

+ 9
- 10
lib/ios/RNNComponentViewController.m View File

@@ -29,18 +29,14 @@
29 29
 	[self.parentViewController onChildWillAppear];
30 30
 }
31 31
 
32
--(void)viewDidAppear:(BOOL)animated {
33
-	[super viewDidAppear:animated];
34
-	[self.eventEmitter sendComponentDidAppear:self.layoutInfo.componentId componentName:self.layoutInfo.name];
35
-}
36
-
37
-- (void)viewWillDisappear:(BOOL)animated {
38
-	[super viewWillDisappear:animated];
32
+- (void)viewDidAppear:(BOOL)animated {
33
+    [super viewDidAppear:animated];
34
+    [self componentDidAppear];
39 35
 }
40 36
 
41 37
 - (void)viewDidDisappear:(BOOL)animated {
42
-	[super viewDidDisappear:animated];
43
-	[self.eventEmitter sendComponentDidDisappear:self.layoutInfo.componentId componentName:self.layoutInfo.name];
38
+    [super viewDidDisappear:animated];
39
+    [self componentDidDisappear];
44 40
 }
45 41
 
46 42
 - (void)loadView {
@@ -56,7 +52,10 @@
56 52
 
57 53
 - (void)renderReactViewIfNeeded {
58 54
     if (!self.isViewLoaded) {
59
-        self.view = [self.creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId reactViewReadyBlock:^{
55
+        self.view = [self.creator createRootView:self.layoutInfo.name
56
+                                      rootViewId:self.layoutInfo.componentId
57
+                                          ofType:RNNComponentTypeComponent
58
+                             reactViewReadyBlock:^{
60 59
             [self->_presenter renderComponents:self.resolveOptions perform:^{
61 60
                 [self readyForPresentation];
62 61
             }];

+ 11
- 5
lib/ios/RNNComponentViewCreator.h View File

@@ -2,14 +2,20 @@
2 2
 #import <UIKit/UIKit.h>
3 3
 #import "RNNComponentOptions.h"
4 4
 #import "RNNReactView.h"
5
+#import "RNNReactButtonView.h"
6
+#import "RNNReactTitleView.h"
7
+#import "RNNReactBackgroundView.h"
5 8
 
6
-@protocol RNNComponentViewCreator
7
-
8
-- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
9
+typedef enum RNNComponentType {
10
+    RNNComponentTypeComponent,
11
+    RNNComponentTypeTopBarTitle,
12
+    RNNComponentTypeTopBarButton,
13
+    RNNComponentTypeTopBarBackground
14
+} RNNComponentType;
9 15
 
10
-- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions;
16
+@protocol RNNComponentViewCreator
11 17
 
12
-- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
18
+- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId ofType:(RNNComponentType)componentType reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
13 19
 
14 20
 @end
15 21
 

+ 2
- 3
lib/ios/RNNEventEmitter.h View File

@@ -1,5 +1,4 @@
1 1
 #import <Foundation/Foundation.h>
2
-
3 2
 #import <React/RCTEventEmitter.h>
4 3
 #import <React/RCTBridgeModule.h>
5 4
 
@@ -7,9 +6,9 @@
7 6
 
8 7
 - (void)sendOnAppLaunched;
9 8
 
10
-- (void)sendComponentDidAppear:(NSString*)componentId componentName:(NSString*)componentName;
9
+- (void)sendComponentDidAppear:(NSString*)componentId componentName:(NSString*)componentName componentType:(NSString *)componentType;
11 10
 
12
-- (void)sendComponentDidDisappear:(NSString*)componentId componentName:(NSString*)componentName;
11
+- (void)sendComponentDidDisappear:(NSString *)componentId componentName:(NSString *)componentName componentType:(NSString *)componentType;
13 12
 
14 13
 - (void)sendOnNavigationButtonPressed:(NSString*)componentId buttonId:(NSString*)buttonId;
15 14
 

+ 6
- 4
lib/ios/RNNEventEmitter.m View File

@@ -44,17 +44,19 @@ static NSString* const ScreenPopped             = @"RNN.ScreenPopped";
44 44
     }
45 45
 }
46 46
 
47
-- (void)sendComponentDidAppear:(NSString *)componentId componentName:(NSString *)componentName {
47
+- (void)sendComponentDidAppear:(NSString *)componentId componentName:(NSString *)componentName componentType:(NSString *)componentType {
48 48
     [self send:ComponentDidAppear body:@{
49 49
         @"componentId":componentId,
50
-        @"componentName": componentName
50
+        @"componentName": componentName,
51
+        @"componentType": componentType
51 52
     }];
52 53
 }
53 54
 
54
-- (void)sendComponentDidDisappear:(NSString *)componentId componentName:(NSString *)componentName{
55
+- (void)sendComponentDidDisappear:(NSString *)componentId componentName:(NSString *)componentName componentType:(NSString *)componentType {
55 56
     [self send:ComponentDidDisappear body:@{
56 57
         @"componentId":componentId,
57
-        @"componentName": componentName
58
+        @"componentName": componentName,
59
+        @"componentType": componentType
58 60
     }];
59 61
 }
60 62
 

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

@@ -10,6 +10,10 @@
10 10
 
11 11
 -(void)applyLeftButtons:(NSArray*)leftButtons rightButtons:(NSArray*)rightButtons defaultLeftButtonStyle:(RNNButtonOptions *)defaultLeftButtonStyle defaultRightButtonStyle:(RNNButtonOptions *)defaultRightButtonStyle;
12 12
 
13
+- (void)componentDidAppear;
14
+
15
+- (void)componentDidDisappear;
16
+
13 17
 @end
14 18
 
15 19
 

+ 27
- 1
lib/ios/RNNNavigationButtons.m View File

@@ -63,6 +63,31 @@
63 63
         [self clearPreviousButtonViews:barButtonItems oldButtons:self.viewController.navigationItem.rightBarButtonItems];
64 64
         [self.viewController.navigationItem setRightBarButtonItems:barButtonItems animated:animated];
65 65
     }
66
+    
67
+    [self notifyButtonsDidAppear:barButtonItems];
68
+}
69
+
70
+- (NSArray *)currentButtons {
71
+    NSArray* currentButtons = [self.viewController.navigationItem.leftBarButtonItems arrayByAddingObjectsFromArray:self.viewController.navigationItem.rightBarButtonItems];
72
+    return currentButtons;
73
+}
74
+
75
+- (void)componentDidAppear {
76
+    for (RNNUIBarButtonItem* barButtonItem in [self currentButtons]) {
77
+        [barButtonItem notifyDidAppear];
78
+    }
79
+}
80
+
81
+- (void)componentDidDisappear {
82
+    for (RNNUIBarButtonItem* barButtonItem in [self currentButtons]) {
83
+        [barButtonItem notifyDidDisappear];
84
+    }
85
+}
86
+
87
+- (void)notifyButtonsDidAppear:(NSArray *)barButtonItems {
88
+    for (RNNUIBarButtonItem* barButtonItem in barButtonItems) {
89
+        [barButtonItem notifyDidAppear];
90
+    }
66 91
 }
67 92
 
68 93
 - (void)clearPreviousButtonViews:(NSArray<UIBarButtonItem *> *)newButtons oldButtons:(NSArray<UIBarButtonItem *> *)oldButtons {
@@ -71,6 +96,7 @@
71 96
     for (UIBarButtonItem* buttonItem in removedButtons) {
72 97
         RNNReactView* reactView = buttonItem.customView;
73 98
         if ([reactView isKindOfClass:[RNNReactView class]]) {
99
+            [reactView componentDidDisappear];
74 100
             [_componentRegistry removeChildComponent:reactView.componentId];
75 101
         }
76 102
     }
@@ -117,7 +143,7 @@
117 143
         componentOptions.componentId = [[Text alloc] initWithValue:component[@"componentId"]];
118 144
         componentOptions.name = [[Text alloc] initWithValue:component[@"name"]];
119 145
         
120
-        RNNReactView *view = [_componentRegistry createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil];
146
+        RNNReactButtonView *view = [_componentRegistry createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId componentType:RNNComponentTypeTopBarButton reactViewReadyBlock:nil];
121 147
         barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view];
122 148
     } else if (iconImage) {
123 149
         barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage];

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

@@ -0,0 +1,5 @@
1
+#import "RNNReactTitleView.h"
2
+
3
+@interface RNNReactBackgroundView : RNNReactTitleView
4
+
5
+@end

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

@@ -0,0 +1,9 @@
1
+#import "RNNReactBackgroundView.h"
2
+
3
+@implementation RNNReactBackgroundView
4
+
5
+- (NSString *)componentType {
6
+	return ComponentTypeBackground;
7
+}
8
+
9
+@end

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

@@ -0,0 +1,5 @@
1
+#import "RNNReactView.h"
2
+
3
+@interface RNNReactButtonView : RNNReactView
4
+
5
+@end

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

@@ -0,0 +1,9 @@
1
+#import "RNNReactButtonView.h"
2
+
3
+@implementation RNNReactButtonView
4
+
5
+- (NSString *)componentType {
6
+    return ComponentTypeButton;
7
+}
8
+
9
+@end

+ 3
- 2
lib/ios/RNNReactComponentRegistry.h View File

@@ -1,5 +1,6 @@
1 1
 #import <Foundation/Foundation.h>
2
-#import "RNNReactView.h"
2
+#import "RNNReactButtonView.h"
3
+#import "RNNReactTitleView.h"
3 4
 #import "RNNComponentOptions.h"
4 5
 #import "RNNComponentViewCreator.h"
5 6
 
@@ -7,7 +8,7 @@
7 8
 
8 9
 - (instancetype)initWithCreator:(id<RNNComponentViewCreator>)creator;
9 10
 
10
-- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
11
+- (RNNReactButtonView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId componentType:(RNNComponentType)componentType reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
11 12
 
12 13
 - (void)removeComponent:(NSString *)componentId;
13 14
 

+ 20
- 12
lib/ios/RNNReactComponentRegistry.m View File

@@ -16,18 +16,26 @@
16 16
 	return self;
17 17
 }
18 18
 
19
-- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
20
-	NSMapTable* parentComponentDict = [self componentsForParentId:parentComponentId];
21
-	
22
-	RNNReactView* reactView = [parentComponentDict objectForKey:component.componentId.get];
23
-	if (!reactView) {
24
-		reactView = (RNNReactView *)[_creator createRootViewFromComponentOptions:component reactViewReadyBlock:reactViewReadyBlock];
25
-        [parentComponentDict setObject:reactView forKey:component.componentId.get];
26
-	} else if (reactViewReadyBlock) {
27
-		reactViewReadyBlock();
28
-	}
29
-	
30
-	return reactView;
19
+- (RNNReactButtonView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId componentType:(RNNComponentType)componentType reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
20
+    RNNReactView* reactView = [self findComponent:component.componentId.get parentComponentId:parentComponentId];
21
+    if (!reactView) {
22
+        reactView = [_creator createRootView:component.name.get rootViewId:component.componentId.get ofType:componentType reactViewReadyBlock:reactViewReadyBlock];
23
+        [self storeComponent:reactView componentId:component.componentId.get parentComponentId:parentComponentId];
24
+    } else if (reactViewReadyBlock) {
25
+        reactViewReadyBlock();
26
+    }
27
+    
28
+    return (RNNReactButtonView *)reactView;
29
+}
30
+
31
+- (RNNReactView *)findComponent:(NSString *)componentId parentComponentId:(NSString *)parentComponentId {
32
+    NSMapTable* parentComponentDict = [self componentsForParentId:parentComponentId];
33
+    return [parentComponentDict objectForKey:componentId];
34
+}
35
+
36
+- (void)storeComponent:(RNNReactView *)component componentId:(NSString *)componentId parentComponentId:(NSString *)parentComponentId {
37
+    NSMapTable* parentComponentDict = [self componentsForParentId:parentComponentId];
38
+    [parentComponentDict setObject:component forKey:componentId];
31 39
 }
32 40
 
33 41
 - (NSMapTable *)componentsForParentId:(NSString *)parentComponentId {

+ 2
- 2
lib/ios/RNNReactRootViewCreator.h View File

@@ -1,10 +1,10 @@
1 1
 #import <Foundation/Foundation.h>
2 2
 #import "RNNComponentViewCreator.h"
3
-
3
+#import "RNNEventEmitter.h"
4 4
 #import <React/RCTBridge.h>
5 5
 
6 6
 @interface RNNReactRootViewCreator : NSObject <RNNComponentViewCreator>
7 7
 
8
--(instancetype)initWithBridge:(RCTBridge*)bridge;
8
+-(instancetype)initWithBridge:(RCTBridge*)bridge eventEmitter:(RNNEventEmitter*)eventEmitter;
9 9
 
10 10
 @end

+ 26
- 17
lib/ios/RNNReactRootViewCreator.m View File

@@ -4,34 +4,43 @@
4 4
 
5 5
 @implementation RNNReactRootViewCreator {
6 6
 	RCTBridge *_bridge;
7
+    RNNEventEmitter* _eventEmitter;
7 8
 }
8 9
 
9
-- (instancetype)initWithBridge:(RCTBridge*)bridge {
10
+- (instancetype)initWithBridge:(RCTBridge*)bridge eventEmitter:(RNNEventEmitter*)eventEmitter {
10 11
 	self = [super init];
11
-	
12 12
 	_bridge = bridge;
13
-	
13
+    _eventEmitter = eventEmitter;
14 14
 	return self;
15 15
 }
16 16
 
17
-- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
18
-	if (!rootViewId) {
19
-		@throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil];
20
-	}
21
-	
22
-	RNNReactView *view = [[RNNReactView alloc] initWithBridge:_bridge
23
-												   moduleName:name
24
-											initialProperties:@{@"componentId": rootViewId}
25
-										  reactViewReadyBlock:reactViewReadyBlock];
26
-	return view;
17
+- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId ofType:(RNNComponentType)componentType reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
18
+    [self verifyRootViewId:rootViewId];
19
+    return [[[self resolveComponentViewClass:componentType] alloc] initWithBridge:_bridge
20
+             moduleName:name
21
+      initialProperties:@{@"componentId": rootViewId}
22
+           eventEmitter:_eventEmitter
23
+    reactViewReadyBlock:reactViewReadyBlock];
27 24
 }
28 25
 
29
-- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions {
30
-	return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:nil];
26
+- (Class)resolveComponentViewClass:(RNNComponentType)componentType {
27
+    switch (componentType) {
28
+        case RNNComponentTypeTopBarTitle:
29
+            return RNNReactTitleView.class;
30
+        case RNNComponentTypeTopBarButton:
31
+            return RNNReactButtonView.class;
32
+        case RNNComponentTypeTopBarBackground:
33
+            return RNNReactBackgroundView.class;
34
+        case RNNComponentTypeComponent:
35
+        default:
36
+            return RNNReactView.class;
37
+    }
31 38
 }
32 39
 
33
-- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
34
-	return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:reactViewReadyBlock];
40
+- (void)verifyRootViewId:(NSString *)rootViewId {
41
+    if (!rootViewId) {
42
+        @throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil];
43
+    }
35 44
 }
36 45
 
37 46
 @end

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

@@ -0,0 +1,9 @@
1
+#import "RNNReactView.h"
2
+
3
+@interface RNNReactTitleView : RNNReactView <RCTRootViewDelegate>
4
+
5
+- (void)setAlignment:(NSString *)alignment inFrame:(CGRect)frame;
6
+
7
+@property (nonatomic, copy) void (^rootViewDidChangeIntrinsicSize)(CGSize intrinsicSize);
8
+
9
+@end

+ 44
- 0
lib/ios/RNNReactTitleView.m View File

@@ -0,0 +1,44 @@
1
+#import "RNNReactTitleView.h"
2
+
3
+@implementation RNNReactTitleView {
4
+    BOOL _fillParent;
5
+}
6
+
7
+- (NSString *)componentType {
8
+    return ComponentTypeTitle;
9
+}
10
+
11
+- (CGSize)intrinsicContentSize {
12
+    if (_fillParent) {
13
+        return UILayoutFittingExpandedSize;
14
+    } else {
15
+        return [super intrinsicContentSize];
16
+    }
17
+}
18
+
19
+- (void)setAlignment:(NSString *)alignment inFrame:(CGRect)frame {
20
+    if ([alignment isEqualToString:@"fill"]) {
21
+        _fillParent = YES;
22
+        self.frame = frame;
23
+        self.sizeFlexibility = RCTRootViewSizeFlexibilityNone;
24
+    } else {
25
+        self.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight;
26
+        __weak RNNReactView *weakSelf = self;
27
+        [self setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicSize) {
28
+            [weakSelf setFrame:CGRectMake(0, 0, intrinsicSize.width, intrinsicSize.height)];
29
+        }];
30
+    }
31
+}
32
+
33
+- (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize {
34
+        _rootViewDidChangeIntrinsicSize = rootViewDidChangeIntrinsicSize;
35
+        self.delegate = self;
36
+}
37
+
38
+- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView {
39
+    if (_rootViewDidChangeIntrinsicSize) {
40
+        _rootViewDidChangeIntrinsicSize(rootView.intrinsicContentSize);
41
+    }
42
+}
43
+
44
+@end

+ 15
- 5
lib/ios/RNNReactView.h View File

@@ -1,17 +1,27 @@
1 1
 #import <React/RCTRootView.h>
2 2
 #import <React/RCTRootViewDelegate.h>
3
+#import "RNNEventEmitter.h"
4
+
5
+#define ComponentTypeScreen @"Component"
6
+#define ComponentTypeTitle @"TopBarTitle"
7
+#define ComponentTypeButton @"TopBarButton"
8
+#define ComponentTypeBackground @"TopBarBackground"
3 9
 
4 10
 typedef void (^RNNReactViewReadyCompletionBlock)(void);
5 11
 
6
-@interface RNNReactView : RCTRootView <RCTRootViewDelegate>
12
+@interface RNNReactView : RCTRootView
7 13
 
8
-- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
14
+- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties eventEmitter:(RNNEventEmitter *)eventEmitter reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
9 15
 
10
-@property (nonatomic, copy) void (^rootViewDidChangeIntrinsicSize)(CGSize intrinsicSize);
11 16
 @property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock;
12
-
13
-- (void)setAlignment:(NSString *)alignment inFrame:(CGRect)frame;
17
+@property (nonatomic, strong) RNNEventEmitter* eventEmitter;
14 18
 
15 19
 - (NSString *)componentId;
16 20
 
21
+- (NSString *)componentType;
22
+
23
+- (void)componentDidAppear;
24
+
25
+- (void)componentDidDisappear;
26
+
17 27
 @end

+ 29
- 35
lib/ios/RNNReactView.m View File

@@ -3,14 +3,15 @@
3 3
 #import <React/RCTUIManager.h>
4 4
 
5 5
 @implementation RNNReactView {
6
-	BOOL _fillParent;
6
+    BOOL _isAppeared;
7 7
 }
8 8
 
9
-- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
9
+- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties eventEmitter:(RNNEventEmitter *)eventEmitter reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
10 10
 	self = [super initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
11 11
 	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentDidAppear:) name:RCTContentDidAppearNotification object:nil];
12 12
 	 _reactViewReadyBlock = reactViewReadyBlock;
13
-	
13
+    _eventEmitter = eventEmitter;
14
+    
14 15
 	return self;
15 16
 }
16 17
 
@@ -23,49 +24,42 @@
23 24
 
24 25
 - (void)contentDidAppear:(NSNotification *)notification {
25 26
 	RNNReactView* appearedView = notification.object;
26
-	
27
-	 if (_reactViewReadyBlock && [appearedView.appProperties[@"componentId"] isEqual:self.componentId]) {
28
-	 	_reactViewReadyBlock();
29
-		 _reactViewReadyBlock = nil;
30
-		 [[NSNotificationCenter defaultCenter] removeObserver:self];
27
+	 if ([appearedView.appProperties[@"componentId"] isEqual:self.componentId]) {
28
+         [self reactViewReady];
31 29
 	 }
32 30
 }
33 31
 
34
-- (NSString *)componentId {
35
-	return self.appProperties[@"componentId"];
32
+- (void)reactViewReady {
33
+    if (_reactViewReadyBlock) {
34
+        _reactViewReadyBlock();
35
+        _reactViewReadyBlock = nil;
36
+    }
37
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
36 38
 }
37 39
 
38
-- (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize {
39
-		_rootViewDidChangeIntrinsicSize = rootViewDidChangeIntrinsicSize;
40
-		self.delegate = self;
40
+
41
+- (void)componentDidAppear {
42
+    if (!_isAppeared) {
43
+        [_eventEmitter sendComponentDidAppear:self.componentId componentName:self.moduleName componentType:self.componentType];
44
+    }
45
+    
46
+    _isAppeared = YES;
41 47
 }
42 48
 
43
-- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView {
44
-	if (_rootViewDidChangeIntrinsicSize) {
45
-		_rootViewDidChangeIntrinsicSize(rootView.intrinsicContentSize);
46
-	}
49
+- (void)componentDidDisappear {
50
+    if (_isAppeared) {
51
+        [_eventEmitter sendComponentDidDisappear:self.componentId componentName:self.moduleName componentType:self.componentType];
52
+    }
53
+    
54
+    _isAppeared = NO;
47 55
 }
48 56
 
49
-- (CGSize)intrinsicContentSize {
50
-	if (_fillParent) {
51
-		return UILayoutFittingExpandedSize;
52
-	} else {
53
-		return [super intrinsicContentSize];
54
-	}
57
+- (NSString *)componentId {
58
+	return self.appProperties[@"componentId"];
55 59
 }
56 60
 
57
-- (void)setAlignment:(NSString *)alignment inFrame:(CGRect)frame {
58
-    if ([alignment isEqualToString:@"fill"]) {
59
-        _fillParent = YES;
60
-        self.frame = frame;
61
-        self.sizeFlexibility = RCTRootViewSizeFlexibilityNone;
62
-    } else {
63
-        self.sizeFlexibility = RCTRootViewSizeFlexibilityWidthAndHeight;
64
-        __weak RNNReactView *weakSelf = self;
65
-        [self setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicSize) {
66
-            [weakSelf setFrame:CGRectMake(0, 0, intrinsicSize.width, intrinsicSize.height)];
67
-        }];
68
-    }
61
+- (NSString *)componentType {
62
+    return ComponentTypeScreen;
69 63
 }
70 64
 
71 65
 @end

+ 15
- 43
lib/ios/RNNStackPresenter.m View File

@@ -3,12 +3,12 @@
3 3
 #import "RNNStackController.h"
4 4
 #import "RNNCustomTitleView.h"
5 5
 #import "TopBarPresenterCreator.h"
6
+#import "RNNReactBackgroundView.h"
6 7
 
7 8
 @interface RNNStackPresenter() {
8 9
 	RNNReactComponentRegistry* _componentRegistry;
9
-	UIView* _customTopBar;
10 10
 	UIView* _customTopBarBackground;
11
-	RNNReactView* _customTopBarBackgroundReactView;
11
+	RNNReactView* _topBarBackgroundReactView;
12 12
     TopBarPresenter* _topBarPresenter;
13 13
 }
14 14
 
@@ -28,6 +28,14 @@
28 28
     _topBarPresenter = [TopBarPresenterCreator createWithBoundedNavigationController:self.stackController];
29 29
 }
30 30
 
31
+- (void)componentDidAppear {
32
+    [_topBarBackgroundReactView componentDidAppear];
33
+}
34
+
35
+- (void)componentDidDisappear {
36
+    [_topBarBackgroundReactView componentDidDisappear];
37
+}
38
+
31 39
 - (RNNStackController *)stackController {
32 40
     return (RNNStackController *)self.boundViewController;
33 41
 }
@@ -115,10 +123,6 @@
115 123
 		[stack setBackButtonColor:options.topBar.backButton.color.get];
116 124
 	}
117 125
 
118
-	if (options.topBar.component.name.hasValue) {
119
-		[self setCustomNavigationBarView:options perform:nil];
120
-	}
121
-	
122 126
 	if (options.topBar.background.component.name.hasValue) {
123 127
 		[self setCustomNavigationComponentBackground:options perform:nil];
124 128
 	}
@@ -131,13 +135,6 @@
131 135
 	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
132 136
 		dispatch_group_t group = dispatch_group_create();
133 137
 		
134
-		dispatch_group_enter(group);
135
-		dispatch_async(dispatch_get_main_queue(), ^{
136
-			[self setCustomNavigationBarView:options perform:^{
137
-				dispatch_group_leave(group);
138
-			}];
139
-		});
140
-		
141 138
 		dispatch_group_enter(group);
142 139
 		dispatch_async(dispatch_get_main_queue(), ^{
143 140
 			[self setCustomNavigationComponentBackground:options perform:^{
@@ -155,32 +152,6 @@
155 152
 	});
156 153
 }
157 154
 
158
-- (void)setCustomNavigationBarView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
159
-	RNNStackController* stack = self.stackController;
160
-	if (![options.topBar.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
161
-		readyBlock();
162
-		readyBlock = nil;
163
-	}
164
-	if (options.topBar.component.name.hasValue) {
165
-		NSString* currentChildComponentId = [stack getCurrentChild].layoutInfo.componentId;
166
-		RCTRootView *reactView = [_componentRegistry createComponentIfNotExists:options.topBar.component parentComponentId:currentChildComponentId reactViewReadyBlock:readyBlock];
167
-		
168
-		if (_customTopBar) {
169
-			[_customTopBar removeFromSuperview];
170
-		}
171
-		_customTopBar = [[RNNCustomTitleView alloc] initWithFrame:stack.navigationBar.bounds subView:reactView alignment:@"fill"];
172
-		reactView.backgroundColor = UIColor.clearColor;
173
-		_customTopBar.backgroundColor = UIColor.clearColor;
174
-		[stack.navigationBar addSubview:_customTopBar];
175
-	} else {
176
-		[_customTopBar removeFromSuperview];
177
-		_customTopBar = nil;
178
-		if (readyBlock) {
179
-			readyBlock();
180
-		}
181
-	}
182
-}
183
-
184 155
 - (void)setCustomNavigationComponentBackground:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
185 156
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
186 157
 	RNNStackController* stack = self.stackController;
@@ -188,12 +159,13 @@
188 159
 		readyBlock();
189 160
 		readyBlock = nil;
190 161
 	}
162
+    
191 163
 	if (withDefault.topBar.background.component.name.hasValue) {
192 164
 		NSString* currentChildComponentId = [stack getCurrentChild].layoutInfo.componentId;
193
-		RNNReactView *reactView = [_componentRegistry createComponentIfNotExists:withDefault.topBar.background.component parentComponentId:currentChildComponentId reactViewReadyBlock:readyBlock];
194
-		_customTopBarBackgroundReactView = reactView;
165
+		_topBarBackgroundReactView = [_componentRegistry createComponentIfNotExists:withDefault.topBar.background.component parentComponentId:currentChildComponentId componentType:RNNComponentTypeTopBarBackground reactViewReadyBlock:readyBlock];
195 166
 		
196 167
 	} else {
168
+        [_topBarBackgroundReactView componentDidDisappear];
197 169
 		[_customTopBarBackground removeFromSuperview];
198 170
 		_customTopBarBackground = nil;
199 171
 		if (readyBlock) {
@@ -207,10 +179,10 @@
207 179
 	if (_customTopBarBackground) {
208 180
 		[_customTopBarBackground removeFromSuperview];
209 181
 	}
210
-	RNNCustomTitleView* customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:stack.navigationBar.bounds subView:_customTopBarBackgroundReactView alignment:@"fill"];
211
-	_customTopBarBackground = customTopBarBackground;
182
+	_customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:stack.navigationBar.bounds subView:_topBarBackgroundReactView alignment:@"fill"];
212 183
 	
213 184
 	[stack.navigationBar insertSubview:_customTopBarBackground atIndex:1];
185
+    [_topBarBackgroundReactView componentDidAppear];
214 186
 }
215 187
 
216 188
 - (void)dealloc {

+ 0
- 3
lib/ios/RNNTopBarOptions.h View File

@@ -35,7 +35,4 @@
35 35
 @property (nonatomic, strong) RNNButtonOptions* leftButtonStyle;
36 36
 @property (nonatomic, strong) RNNButtonOptions* rightButtonStyle;
37 37
 
38
-
39
-@property (nonatomic, strong) RNNComponentOptions* component;
40
-
41 38
 @end

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

@@ -36,7 +36,6 @@
36 36
 	self.backButton = [[RNNBackButtonOptions alloc] initWithDict:dict[@"backButton"]];
37 37
 	self.leftButtonStyle = [[RNNButtonOptions alloc] initWithDict:dict[@"leftButtonStyle"]];
38 38
 	self.rightButtonStyle = [[RNNButtonOptions alloc] initWithDict:dict[@"rightButtonStyle"]];
39
-	self.component = [[RNNComponentOptions alloc] initWithDict:dict[@"component"]];
40 39
 	
41 40
 	if (self.leftButtonColor.hasValue) {
42 41
 		self.leftButtonStyle.color = self.leftButtonColor;

+ 3
- 0
lib/ios/RNNUIBarButtonItem.h View File

@@ -12,5 +12,8 @@
12 12
 -(instancetype)init:(NSString*)buttonId withCustomView:(RCTRootView *)reactView;
13 13
 -(instancetype)init:(NSString*)buttonId withSystemItem:(NSString*)systemItemName;
14 14
 
15
+- (void)notifyDidAppear;
16
+- (void)notifyDidDisappear;
17
+
15 18
 @end
16 19
 

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

@@ -63,6 +63,18 @@
63 63
 	return self;
64 64
 }
65 65
 
66
+- (void)notifyDidAppear {
67
+    if ([self.customView isKindOfClass:[RNNReactView class]]) {
68
+        [((RNNReactView *)self.customView) componentDidAppear];
69
+    }
70
+}
71
+
72
+- (void)notifyDidDisappear {
73
+    if ([self.customView isKindOfClass:[RNNReactView class]]) {
74
+        [((RNNReactView *)self.customView) componentDidDisappear];
75
+    }
76
+}
77
+
66 78
 - (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView {
67 79
 	self.widthConstraint.constant = rootView.intrinsicContentSize.width;
68 80
 	self.heightConstraint.constant = rootView.intrinsicContentSize.height;

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

@@ -131,6 +131,12 @@
131 131
 		503955982174864E00B0A663 /* NullDouble.m in Sources */ = {isa = PBXBuildFile; fileRef = 503955962174864E00B0A663 /* NullDouble.m */; };
132 132
 		5039559B2174867000B0A663 /* DoubleParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 503955992174867000B0A663 /* DoubleParser.h */; };
133 133
 		5039559C2174867000B0A663 /* DoubleParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 5039559A2174867000B0A663 /* DoubleParser.m */; };
134
+		503A8A1923BCB2ED0094D1C4 /* RNNReactButtonView.h in Headers */ = {isa = PBXBuildFile; fileRef = 503A8A1723BCB2ED0094D1C4 /* RNNReactButtonView.h */; };
135
+		503A8A1A23BCB2ED0094D1C4 /* RNNReactButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 503A8A1823BCB2ED0094D1C4 /* RNNReactButtonView.m */; };
136
+		503A8A1D23BCB3230094D1C4 /* RNNReactTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 503A8A1B23BCB3230094D1C4 /* RNNReactTitleView.h */; };
137
+		503A8A1E23BCB3230094D1C4 /* RNNReactTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 503A8A1C23BCB3230094D1C4 /* RNNReactTitleView.m */; };
138
+		503A8A2123BCE9C60094D1C4 /* RNNReactBackgroundView.h in Headers */ = {isa = PBXBuildFile; fileRef = 503A8A1F23BCE9C60094D1C4 /* RNNReactBackgroundView.h */; };
139
+		503A8A2223BCE9C60094D1C4 /* RNNReactBackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = 503A8A2023BCE9C60094D1C4 /* RNNReactBackgroundView.m */; };
134 140
 		50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 50415CB820553B8E00BB682E /* RNNScreenTransition.h */; };
135 141
 		50415CBB20553B8E00BB682E /* RNNScreenTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 50415CB920553B8E00BB682E /* RNNScreenTransition.m */; };
136 142
 		50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50451D032042DAEB00695F00 /* RNNPushAnimation.h */; };
@@ -513,6 +519,12 @@
513 519
 		503955962174864E00B0A663 /* NullDouble.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NullDouble.m; sourceTree = "<group>"; };
514 520
 		503955992174867000B0A663 /* DoubleParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DoubleParser.h; sourceTree = "<group>"; };
515 521
 		5039559A2174867000B0A663 /* DoubleParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DoubleParser.m; sourceTree = "<group>"; };
522
+		503A8A1723BCB2ED0094D1C4 /* RNNReactButtonView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactButtonView.h; sourceTree = "<group>"; };
523
+		503A8A1823BCB2ED0094D1C4 /* RNNReactButtonView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactButtonView.m; sourceTree = "<group>"; };
524
+		503A8A1B23BCB3230094D1C4 /* RNNReactTitleView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactTitleView.h; sourceTree = "<group>"; };
525
+		503A8A1C23BCB3230094D1C4 /* RNNReactTitleView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactTitleView.m; sourceTree = "<group>"; };
526
+		503A8A1F23BCE9C60094D1C4 /* RNNReactBackgroundView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactBackgroundView.h; sourceTree = "<group>"; };
527
+		503A8A2023BCE9C60094D1C4 /* RNNReactBackgroundView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactBackgroundView.m; sourceTree = "<group>"; };
516 528
 		50415CB820553B8E00BB682E /* RNNScreenTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNScreenTransition.h; sourceTree = "<group>"; };
517 529
 		50415CB920553B8E00BB682E /* RNNScreenTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNScreenTransition.m; sourceTree = "<group>"; };
518 530
 		50451D032042DAEB00695F00 /* RNNPushAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNPushAnimation.h; sourceTree = "<group>"; };
@@ -1138,6 +1150,12 @@
1138 1150
 				50CED450239F9DFC00C42EE2 /* TopBarPresenter.m */,
1139 1151
 				501E0215213E7EA3003365C5 /* RNNReactView.h */,
1140 1152
 				501E0216213E7EA3003365C5 /* RNNReactView.m */,
1153
+				503A8A1723BCB2ED0094D1C4 /* RNNReactButtonView.h */,
1154
+				503A8A1823BCB2ED0094D1C4 /* RNNReactButtonView.m */,
1155
+				503A8A1B23BCB3230094D1C4 /* RNNReactTitleView.h */,
1156
+				503A8A1C23BCB3230094D1C4 /* RNNReactTitleView.m */,
1157
+				503A8A1F23BCE9C60094D1C4 /* RNNReactBackgroundView.h */,
1158
+				503A8A2023BCE9C60094D1C4 /* RNNReactBackgroundView.m */,
1141 1159
 				26916C961E4B9E7700D13680 /* RNNReactRootViewCreator.h */,
1142 1160
 				26916C971E4B9E7700D13680 /* RNNReactRootViewCreator.m */,
1143 1161
 				7BA500761E254908001B9E1B /* RNNSplashScreen.h */,
@@ -1364,9 +1382,11 @@
1364 1382
 				5060DE73219DAD7E00D0C052 /* ReactNativeNavigation.h in Headers */,
1365 1383
 				261F0E6A1E6F028A00989DE2 /* RNNNavigationStackManager.h in Headers */,
1366 1384
 				50EB4ED72068EBE000D6ED34 /* RNNBackgroundOptions.h in Headers */,
1385
+				503A8A2123BCE9C60094D1C4 /* RNNReactBackgroundView.h in Headers */,
1367 1386
 				504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */,
1368 1387
 				504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */,
1369 1388
 				5038A3CE216E35E0009280BC /* Dictionary.h in Headers */,
1389
+				503A8A1D23BCB3230094D1C4 /* RNNReactTitleView.h in Headers */,
1370 1390
 				509416A323A11C340036092C /* Enum.h in Headers */,
1371 1391
 				26916C981E4B9E7700D13680 /* RNNReactRootViewCreator.h in Headers */,
1372 1392
 				5012241A21736678000F5F98 /* Image.h in Headers */,
@@ -1406,6 +1426,7 @@
1406 1426
 				507F43C51FF4F17C00D9425B /* RNNTopTabsViewController.h in Headers */,
1407 1427
 				501223D72173590F000F5F98 /* RNNStackPresenter.h in Headers */,
1408 1428
 				50495946216F5FB5006D2B81 /* TextParser.h in Headers */,
1429
+				503A8A1923BCB2ED0094D1C4 /* RNNReactButtonView.h in Headers */,
1409 1430
 				505EDD3C214FA8000071C7DE /* RNNComponentPresenter.h in Headers */,
1410 1431
 				502CB46E20CD1DDA0019B2FE /* RNNBackButtonOptions.h in Headers */,
1411 1432
 				50495939216E5750006D2B81 /* Bool.h in Headers */,
@@ -1665,6 +1686,7 @@
1665 1686
 				5016E8F020209690009D4F7C /* RNNCustomTitleView.m in Sources */,
1666 1687
 				503955982174864E00B0A663 /* NullDouble.m in Sources */,
1667 1688
 				E8DA24411F97459B00CD552B /* RNNElementFinder.m in Sources */,
1689
+				503A8A2223BCE9C60094D1C4 /* RNNReactBackgroundView.m in Sources */,
1668 1690
 				50570B272061473D006A1B5C /* RNNTitleOptions.m in Sources */,
1669 1691
 				5047E4F122674AD400908DD3 /* RNNLayoutManager.m in Sources */,
1670 1692
 				5012241F217366D4000F5F98 /* ColorParser.m in Sources */,
@@ -1677,6 +1699,7 @@
1677 1699
 				A7626BFD1FC2FB2C00492FB8 /* RNNTopBarOptions.m in Sources */,
1678 1700
 				5050465521F8F4490035497A /* RNNReactComponentRegistry.m in Sources */,
1679 1701
 				509416AC23A11CB20036092C /* NullEnum.m in Sources */,
1702
+				503A8A1A23BCB2ED0094D1C4 /* RNNReactButtonView.m in Sources */,
1680 1703
 				50570BEB2063E09B006A1B5C /* RNNTitleViewHelper.m in Sources */,
1681 1704
 				263905E71E4CAC950023D7D3 /* RNNSideMenuChildVC.m in Sources */,
1682 1705
 				50495957216F6B3D006D2B81 /* DictionaryParser.m in Sources */,
@@ -1775,6 +1798,7 @@
1775 1798
 				5038A3D3216E364C009280BC /* Text.m in Sources */,
1776 1799
 				5017D9E2239D2C6C00B74047 /* BottomTabsAttachModeFactory.m in Sources */,
1777 1800
 				5012240F21735999000F5F98 /* RNNBasePresenter.m in Sources */,
1801
+				503A8A1E23BCB3230094D1C4 /* RNNReactTitleView.m in Sources */,
1778 1802
 				5038A375216CDDB6009280BC /* UIViewController+SideMenuController.m in Sources */,
1779 1803
 				E8E518331F83B3E0000467AC /* RNNUtils.m in Sources */,
1780 1804
 				50451D062042DAEB00695F00 /* RNNPushAnimation.m in Sources */,

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

@@ -28,6 +28,10 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
28 28
 
29 29
 - (void)readyForPresentation;
30 30
 
31
+- (void)componentDidAppear;
32
+
33
+- (void)componentDidDisappear;
34
+
31 35
 @property (nonatomic, retain) RNNBasePresenter* presenter;
32 36
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
33 37
 @property (nonatomic, strong) RNNNavigationOptions* options;

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

@@ -114,7 +114,17 @@
114 114
 
115 115
 - (void)onChildWillAppear {
116 116
 	[self.presenter applyOptions:self.resolveOptions];
117
-	[((UISplitViewController *)self.parentViewController) onChildWillAppear];
117
+	[self.parentViewController onChildWillAppear];
118
+}
119
+
120
+- (void)componentDidAppear {
121
+    [self.presenter componentDidAppear];
122
+    [self.parentViewController componentDidAppear];
123
+}
124
+
125
+- (void)componentDidDisappear {
126
+    [self.presenter componentDidDisappear];
127
+    [self.parentViewController componentDidDisappear];
118 128
 }
119 129
 
120 130
 - (void)willMoveToParentViewController:(UIViewController *)parent {

+ 15
- 14
lib/src/events/ComponentEventsObserver.test.tsx View File

@@ -148,7 +148,7 @@ describe('ComponentEventsObserver', () => {
148 148
     expect(tree.toJSON()).toBeDefined();
149 149
     expect(didAppearFn).not.toHaveBeenCalled();
150 150
 
151
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
151
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
152 152
     expect(didAppearFn).toHaveBeenCalledTimes(1);
153 153
   });
154 154
 
@@ -158,11 +158,11 @@ describe('ComponentEventsObserver', () => {
158 158
 
159 159
     expect(tree.toJSON()).toBeDefined();
160 160
     
161
-    uut.notifyComponentDidAppear({ componentId: 'dontUseThisId', componentName: 'doesnt matter' });
161
+    uut.notifyComponentDidAppear({ componentId: 'dontUseThisId', componentName: 'doesnt matter', componentType: 'Component' });
162 162
     expect(didAppearFn).not.toHaveBeenCalled();
163 163
     
164 164
 
165
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
165
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
166 166
     expect(didAppearFn).toHaveBeenCalledTimes(1);
167 167
   });
168 168
 
@@ -174,10 +174,10 @@ describe('ComponentEventsObserver', () => {
174 174
     expect(didDisappearFn).not.toHaveBeenCalled();
175 175
     expect(willUnmountFn).not.toHaveBeenCalled();
176 176
 
177
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
177
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
178 178
     expect(didAppearFn).toHaveBeenCalledTimes(1);
179 179
 
180
-    uut.notifyComponentDidDisappear({ componentId: 'myCompId', componentName: 'doesnt matter' });
180
+    uut.notifyComponentDidDisappear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
181 181
     expect(didDisappearFn).toHaveBeenCalledTimes(1);
182 182
 
183 183
     uut.notifyNavigationButtonPressed({ componentId: 'myCompId', buttonId: 'myButtonId' });
@@ -211,6 +211,7 @@ describe('ComponentEventsObserver', () => {
211 211
   it(`componentDidAppear should receive component props from store`, () => {
212 212
     const event = {
213 213
       componentId: 'myCompId',
214
+      componentType: 'Component',
214 215
       passProps: {
215 216
         propA: 'propA'
216 217
       },
@@ -220,14 +221,14 @@ describe('ComponentEventsObserver', () => {
220 221
     mockStore.updateProps(event.componentId, event.passProps)
221 222
     expect(didAppearFn).not.toHaveBeenCalled();
222 223
 
223
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
224
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
224 225
     expect(didAppearFn).toHaveBeenCalledTimes(1);
225 226
     expect(didAppearFn).toHaveBeenCalledWith(event);
226 227
   });
227 228
 
228 229
   it(`doesnt call other componentIds`, () => {
229 230
     renderer.create(<BoundScreen componentId={'myCompId'} />);
230
-    uut.notifyComponentDidAppear({ componentId: 'other', componentName: 'doesnt matter' });
231
+    uut.notifyComponentDidAppear({ componentId: 'other', componentName: 'doesnt matter', componentType: 'Component' });
231 232
     expect(didAppearFn).not.toHaveBeenCalled();
232 233
   });
233 234
 
@@ -235,18 +236,18 @@ describe('ComponentEventsObserver', () => {
235 236
     const tree = renderer.create(<SimpleScreen componentId={'myCompId'} />);
236 237
     expect((tree.getInstance() as any).componentDidAppear).toBeUndefined();
237 238
     uut.bindComponent(tree.getInstance() as any);
238
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
239
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
239 240
   });
240 241
 
241 242
   it(`returns unregister fn`, () => {
242 243
     renderer.create(<BoundScreen componentId={'123'} />);
243 244
 
244
-    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter' });
245
+    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter', componentType: 'Component' });
245 246
     expect(didAppearFn).toHaveBeenCalledTimes(1);
246 247
 
247 248
     subscription.remove();
248 249
 
249
-    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter' });
250
+    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter', componentType: 'Component' });
250 251
     expect(didAppearFn).toHaveBeenCalledTimes(1);
251 252
   });
252 253
 
@@ -256,7 +257,7 @@ describe('ComponentEventsObserver', () => {
256 257
 
257 258
     uut.unmounted('123');
258 259
 
259
-    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter' });
260
+    uut.notifyComponentDidAppear({ componentId: '123', componentName: 'doesnt matter', componentType: 'Component' });
260 261
     expect(didAppearFn).not.toHaveBeenCalled();
261 262
   });
262 263
 
@@ -272,20 +273,20 @@ describe('ComponentEventsObserver', () => {
272 273
     const result2 = uut.bindComponent(instance2);
273 274
     expect(result1).not.toEqual(result2);
274 275
 
275
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
276
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
276 277
 
277 278
     expect(instance1.componentDidAppear).toHaveBeenCalledTimes(1);
278 279
     expect(instance2.componentDidAppear).toHaveBeenCalledTimes(1);
279 280
 
280 281
     result2.remove();
281 282
 
282
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
283
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
283 284
     expect(instance1.componentDidAppear).toHaveBeenCalledTimes(2);
284 285
     expect(instance2.componentDidAppear).toHaveBeenCalledTimes(1);
285 286
 
286 287
     result1.remove();
287 288
 
288
-    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter' });
289
+    uut.notifyComponentDidAppear({ componentId: 'myCompId', componentName: 'doesnt matter', componentType: 'Component' });
289 290
     expect(instance1.componentDidAppear).toHaveBeenCalledTimes(2);
290 291
     expect(instance2.componentDidAppear).toHaveBeenCalledTimes(1);
291 292
   });

+ 4
- 0
lib/src/interfaces/ComponentEvents.ts View File

@@ -1,3 +1,5 @@
1
+export type ComponentType = 'Component' | 'TopBarTitle' | 'TopBarBackground' | 'TopBarButton';
2
+
1 3
 export interface ComponentEvent {
2 4
   componentId: string;
3 5
 }
@@ -5,10 +7,12 @@ export interface ComponentEvent {
5 7
 export interface ComponentDidAppearEvent extends ComponentEvent {
6 8
   componentName: string;
7 9
   passProps?: object;
10
+  componentType: ComponentType;
8 11
 }
9 12
 
10 13
 export interface ComponentDidDisappearEvent extends ComponentEvent {
11 14
   componentName: string;
15
+  componentType: ComponentType;
12 16
 }
13 17
 
14 18
 export interface NavigationButtonPressedEvent extends ComponentEvent {

+ 6
- 6
playground/ios/NavigationTests/RNNComponentPresenterTest.m View File

@@ -170,7 +170,7 @@
170 170
 	[[(id)self.componentRegistry expect] createComponentIfNotExists:[OCMArg checkWithBlock:^BOOL(RNNComponentOptions* options) {
171 171
 		return [options.name.get isEqual:@"titleComponent"] &&
172 172
 		[options.componentId.get isEqual:@"id"];
173
-	}] parentComponentId:self.uut.boundComponentId reactViewReadyBlock:[OCMArg any]];
173
+	}] parentComponentId:self.uut.boundComponentId componentType:RNNComponentTypeTopBarTitle reactViewReadyBlock:[OCMArg any]];
174 174
 	[self.uut renderComponents:self.options perform:nil];
175 175
 	[(id)self.componentRegistry verify];
176 176
 	
@@ -189,7 +189,7 @@
189 189
 	[[(id)self.componentRegistry expect] createComponentIfNotExists:[OCMArg checkWithBlock:^BOOL(RNNComponentOptions* options) {
190 190
 		return [options.name.get isEqual:@"titleComponent"] &&
191 191
 		[options.componentId.get isEqual:@"id"];
192
-	}] parentComponentId:self.uut.boundComponentId reactViewReadyBlock:[OCMArg any]];
192
+	}] parentComponentId:self.uut.boundComponentId componentType:RNNComponentTypeTopBarTitle reactViewReadyBlock:[OCMArg any]];
193 193
 	[self.uut renderComponents:self.options perform:nil];
194 194
 	[(id)self.componentRegistry verify];
195 195
 	
@@ -198,8 +198,8 @@
198 198
 }
199 199
 
200 200
 - (void)testRemoveTitleComponentIfNeeded_componentIsRemovedIfTitleTextIsDefined {
201
-	id mockTitle = [OCMockObject niceMockForClass:[RNNReactView class]];
202
-    OCMStub([self.componentRegistry createComponentIfNotExists:[OCMArg any] parentComponentId:[OCMArg any] reactViewReadyBlock:nil]).andReturn(mockTitle);
201
+	id mockTitle = [OCMockObject niceMockForClass:[RNNReactTitleView class]];
202
+    OCMStub([self.componentRegistry createComponentIfNotExists:[OCMArg any] parentComponentId:[OCMArg any] componentType:RNNComponentTypeTopBarTitle reactViewReadyBlock:nil]).andReturn(mockTitle);
203 203
 
204 204
 	RNNComponentOptions* component = [RNNComponentOptions new];
205 205
 	component.name = [[Text alloc] initWithValue:@"componentName"];
@@ -219,8 +219,8 @@
219 219
 }
220 220
 
221 221
 - (void)testRemoveTitleComponentIfNeeded_componentIsNotRemovedIfMergeOptionsIsCalledWithoutTitleText {
222
-    id mockTitle = [OCMockObject niceMockForClass:[RNNReactView class]];
223
-    OCMStub([self.componentRegistry createComponentIfNotExists:[OCMArg any] parentComponentId:[OCMArg any] reactViewReadyBlock:nil]).andReturn(mockTitle);
222
+    id mockTitle = [OCMockObject niceMockForClass:[RNNReactTitleView class]];
223
+    OCMStub([self.componentRegistry createComponentIfNotExists:[OCMArg any] parentComponentId:[OCMArg any]  componentType:RNNComponentTypeTopBarTitle reactViewReadyBlock:nil]).andReturn(mockTitle);
224 224
 
225 225
     RNNComponentOptions* component = [RNNComponentOptions new];
226 226
     component.name = [[Text alloc] initWithValue:@"componentName"];

+ 1
- 1
playground/ios/NavigationTests/RNNTestRootViewCreator.m View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 @implementation RNNTestRootViewCreator
4 4
 
5
-- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
5
+- (RNNReactView *)createRootView:(NSString *)name rootViewId:(NSString *)rootViewId ofType:(RNNComponentType)componentType reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
6 6
 	UIView *view = [[UIView alloc] init];
7 7
 	view.tag = [rootViewId intValue];
8 8
 	return view;

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

@@ -14,7 +14,8 @@ const {
14 14
   POP_TO_ROOT_BTN,
15 15
   ADD_BACK_HANDLER,
16 16
   REMOVE_BACK_HANDLER,
17
-  SET_STACK_ROOT_BUTTON
17
+  SET_STACK_ROOT_BUTTON,
18
+  PUSH_OPTIONS_BUTTON
18 19
 } = require('../testIDs');
19 20
 const Screens = require('./Screens');
20 21
 
@@ -56,6 +57,7 @@ class PushedScreen extends React.Component {
56 57
         <Button label='Add BackHandler' testID={ADD_BACK_HANDLER} onPress={this.addBackHandler} />
57 58
         <Button label='Remove BackHandler' testID={REMOVE_BACK_HANDLER} onPress={this.removeBackHandler} />
58 59
         <Button label='Set Stack Root' testID={SET_STACK_ROOT_BUTTON} onPress={this.setStackRoot} />
60
+        <Button label='Push Options Screen' testID={PUSH_OPTIONS_BUTTON} onPress={this.pushOptionsScreen} />
59 61
       </Root>
60 62
     );
61 63
   }
@@ -89,6 +91,12 @@ class PushedScreen extends React.Component {
89 91
     }
90 92
   });
91 93
 
94
+  pushOptionsScreen = () => Navigation.push(this, {
95
+    component: {
96
+      name: Screens.Options
97
+    }
98
+  });
99
+
92 100
   popToFirstScreen = () => Navigation.popTo(this.props.previousScreenIds[0]);
93 101
 
94 102
   popToRoot = () => Navigation.popToRoot(this);

+ 21
- 1
playground/src/screens/StaticLifecycleOverlay.js View File

@@ -60,7 +60,7 @@ class StaticLifecycleOverlay extends Component {
60 60
     } else if (event.commandName) {
61 61
       return <Text style={styles.h2}>{`${event.commandName}`}</Text>;
62 62
     } else if (event.componentName) {
63
-      return <Text style={styles.h2}>{`${event.event} | ${event.componentName}`}</Text>;
63
+      return <Text style={styles.h2}>{`${event.event} | ${event.componentName} | ${event.componentType}`}</Text>;
64 64
     } else if (event.buttonId) {
65 65
       return <Text style={styles.h2}>{`${event.event} | ${event.buttonId}`}</Text>;
66 66
     } else {
@@ -82,6 +82,7 @@ class StaticLifecycleOverlay extends Component {
82 82
           {events}
83 83
         </View>
84 84
         {this.renderDismissButton()}
85
+        {this.renderClearButton()}
85 86
       </View>
86 87
     );
87 88
   }
@@ -96,6 +97,17 @@ class StaticLifecycleOverlay extends Component {
96 97
       </TouchableOpacity>
97 98
     );
98 99
   }
100
+
101
+  renderClearButton = () => {
102
+    return (
103
+      <TouchableOpacity
104
+        style={styles.clearBtn}
105
+        onPress={() => this.setState({events: []})}
106
+      >
107
+        <Text testID={TestIDs.CLEAR_OVERLAY_EVENTS_BTN} style={{ color: 'red', alignSelf: 'center' }}>Clear</Text>
108
+      </TouchableOpacity>
109
+    );
110
+  }
99 111
 }
100 112
 module.exports = StaticLifecycleOverlay;
101 113
 
@@ -116,6 +128,14 @@ const styles = {
116 128
     backgroundColor: 'white',
117 129
     justifyContent: 'center'
118 130
   },
131
+  clearBtn: {
132
+    position: 'absolute',
133
+    right: 0,
134
+    width: 35,
135
+    height: 35,
136
+    backgroundColor: 'white',
137
+    justifyContent: 'center'
138
+  },
119 139
   events: {
120 140
     flexDirection: 'column',
121 141
     marginHorizontal: 2

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

@@ -84,6 +84,7 @@ module.exports = {
84 84
   PORTRAIT_ORIENTATION_BTN: 'PORTRAIT_ORIENTATION_BTN',
85 85
   LANDSCAPE_ORIENTATION_BTN: 'LANDSCAPE_ORIENTATION_BTN',
86 86
   DISMISS_BTN: 'DISMISS_BTN',
87
+  CLEAR_OVERLAY_EVENTS_BTN: 'CLEAR_OVERLAY_EVENTS_BTN',
87 88
   SEARCH_BTN: 'SEARCH_BTN',
88 89
   SET_STACK_ROOT_BTN: 'SET_STACK_ROOT_BTN',
89 90
   SET_STACK_ROOT_WITH_ID_BTN: 'SET_STACK_ROOT_WITH_ID_BTN',