瀏覽代碼

Add screenPopped event (#5748)

Called each time a screen is popped, either due to `Navigation.pop` or pop gesture.
closes #3941 

Navigation.events().registerScreenPoppedListener(({ componentId }) => {

});
Yogev Ben David 5 年之前
父節點
當前提交
71af55968d

+ 13
- 0
docs/docs/events.md 查看文件

145
 modalDismissedListener.remove();
145
 modalDismissedListener.remove();
146
 ```
146
 ```
147
 
147
 
148
+## registerScreenPoppedListener
149
+Invoked when screen is popped.
150
+
151
+```js
152
+// Subscribe
153
+const screenPoppedListener = Navigation.events().registerScreenPoppedListener(({ componentId }) => {
154
+
155
+});
156
+...
157
+// Unsubscribe
158
+screenPoppedListener.remove();
159
+```
160
+
148
 |       Parameter         | Description |
161
 |       Parameter         | Description |
149
 |:--------------------:|:-----|
162
 |:--------------------:|:-----|
150
 |**componentId** | Id of the modal|
163
 |**componentId** | Id of the modal|

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java 查看文件

176
     }
176
     }
177
 
177
 
178
 	private ViewController createStack(LayoutNode node) {
178
 	private ViewController createStack(LayoutNode node) {
179
-        return new StackControllerBuilder(activity)
179
+        return new StackControllerBuilder(activity, eventEmitter)
180
                 .setChildren(createChildren(node.children))
180
                 .setChildren(createChildren(node.children))
181
                 .setChildRegistry(childRegistry)
181
                 .setChildRegistry(childRegistry)
182
                 .setTopBarController(new TopBarController())
182
                 .setTopBarController(new TopBarController())

+ 7
- 0
lib/android/app/src/main/java/com/reactnativenavigation/react/EventEmitter.java 查看文件

19
     private static final String ComponentDidDisappear = "RNN.ComponentDidDisappear";
19
     private static final String ComponentDidDisappear = "RNN.ComponentDidDisappear";
20
     private static final String NavigationButtonPressed = "RNN.NavigationButtonPressed";
20
     private static final String NavigationButtonPressed = "RNN.NavigationButtonPressed";
21
     private static final String ModalDismissed = "RNN.ModalDismissed";
21
     private static final String ModalDismissed = "RNN.ModalDismissed";
22
+    private static final String ScreenPopped = "RNN.ScreenPopped";
22
     @Nullable
23
     @Nullable
23
     private ReactContext reactContext;
24
     private ReactContext reactContext;
24
 
25
 
73
         emit(ModalDismissed, event);
74
         emit(ModalDismissed, event);
74
     }
75
     }
75
 
76
 
77
+    public void emitScreenPoppedEvent(String componentId) {
78
+        WritableMap event = Arguments.createMap();
79
+        event.putString("componentId", componentId);
80
+        emit(ScreenPopped, event);
81
+    }
82
+
76
     private void emit(String eventName) {
83
     private void emit(String eventName) {
77
         emit(eventName, Arguments.createMap());
84
         emit(eventName, Arguments.createMap());
78
     }
85
     }

+ 5
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java 查看文件

9
 import com.reactnativenavigation.presentation.Presenter;
9
 import com.reactnativenavigation.presentation.Presenter;
10
 import com.reactnativenavigation.presentation.StackPresenter;
10
 import com.reactnativenavigation.presentation.StackPresenter;
11
 import com.reactnativenavigation.react.Constants;
11
 import com.reactnativenavigation.react.Constants;
12
+import com.reactnativenavigation.react.EventEmitter;
12
 import com.reactnativenavigation.utils.CommandListener;
13
 import com.reactnativenavigation.utils.CommandListener;
13
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14
 import com.reactnativenavigation.utils.CommandListenerAdapter;
14
 import com.reactnativenavigation.utils.CompatUtils;
15
 import com.reactnativenavigation.utils.CompatUtils;
43
 
44
 
44
     private IdStack<ViewController> stack = new IdStack<>();
45
     private IdStack<ViewController> stack = new IdStack<>();
45
     private final NavigationAnimator animator;
46
     private final NavigationAnimator animator;
47
+    private final EventEmitter eventEmitter;
46
     private TopBarController topBarController;
48
     private TopBarController topBarController;
47
     private BackButtonHelper backButtonHelper;
49
     private BackButtonHelper backButtonHelper;
48
     private final StackPresenter presenter;
50
     private final StackPresenter presenter;
49
 
51
 
50
-    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackPresenter stackPresenter, Presenter presenter) {
52
+    public StackController(Activity activity, List<ViewController> children, ChildControllersRegistry childRegistry, EventEmitter eventEmitter, TopBarController topBarController, NavigationAnimator animator, String id, Options initialOptions, BackButtonHelper backButtonHelper, StackPresenter stackPresenter, Presenter presenter) {
51
         super(activity, childRegistry, id, presenter, initialOptions);
53
         super(activity, childRegistry, id, presenter, initialOptions);
54
+        this.eventEmitter = eventEmitter;
52
         this.topBarController = topBarController;
55
         this.topBarController = topBarController;
53
         this.animator = animator;
56
         this.animator = animator;
54
         this.backButtonHelper = backButtonHelper;
57
         this.backButtonHelper = backButtonHelper;
277
     private void finishPopping(ViewController disappearing, CommandListener listener) {
280
     private void finishPopping(ViewController disappearing, CommandListener listener) {
278
         disappearing.destroy();
281
         disappearing.destroy();
279
         listener.onSuccess(disappearing.getId());
282
         listener.onSuccess(disappearing.getId());
283
+        eventEmitter.emitScreenPoppedEvent(disappearing.getId());
280
     }
284
     }
281
 
285
 
282
     public void popTo(ViewController viewController, Options mergeOptions, CommandListener listener) {
286
     public void popTo(ViewController viewController, Options mergeOptions, CommandListener listener) {

+ 10
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerBuilder.java 查看文件

6
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.Options;
7
 import com.reactnativenavigation.presentation.Presenter;
7
 import com.reactnativenavigation.presentation.Presenter;
8
 import com.reactnativenavigation.presentation.StackPresenter;
8
 import com.reactnativenavigation.presentation.StackPresenter;
9
+import com.reactnativenavigation.react.EventEmitter;
9
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
10
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
12
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
26
     private Presenter presenter;
27
     private Presenter presenter;
27
     private StackPresenter stackPresenter;
28
     private StackPresenter stackPresenter;
28
     private List<ViewController> children = new ArrayList<>();
29
     private List<ViewController> children = new ArrayList<>();
30
+    private EventEmitter eventEmitter;
29
 
31
 
30
-    public StackControllerBuilder(Activity activity) {
32
+    public StackControllerBuilder(Activity activity, EventEmitter eventEmitter) {
31
         this.activity = activity;
33
         this.activity = activity;
34
+        this.eventEmitter = eventEmitter;
32
         presenter = new Presenter(activity, new Options());
35
         presenter = new Presenter(activity, new Options());
33
         animator = new NavigationAnimator(activity, new ElementTransitionManager());
36
         animator = new NavigationAnimator(activity, new ElementTransitionManager());
34
     }
37
     }
35
 
38
 
39
+    public StackControllerBuilder setEventEmitter(EventEmitter eventEmitter) {
40
+        this.eventEmitter = eventEmitter;
41
+        return this;
42
+    }
43
+
36
     public StackControllerBuilder setChildren(ViewController... children) {
44
     public StackControllerBuilder setChildren(ViewController... children) {
37
         return setChildren(Arrays.asList(children));
45
         return setChildren(Arrays.asList(children));
38
     }
46
     }
86
         return new StackController(activity,
94
         return new StackController(activity,
87
                 children,
95
                 children,
88
                 childRegistry,
96
                 childRegistry,
97
+                eventEmitter,
89
                 topBarController,
98
                 topBarController,
90
                 animator,
99
                 animator,
91
                 id,
100
                 id,

+ 2
- 1
lib/android/app/src/test/java/com/reactnativenavigation/TestUtils.java 查看文件

12
 import com.reactnativenavigation.parse.params.Bool;
12
 import com.reactnativenavigation.parse.params.Bool;
13
 import com.reactnativenavigation.presentation.RenderChecker;
13
 import com.reactnativenavigation.presentation.RenderChecker;
14
 import com.reactnativenavigation.presentation.StackPresenter;
14
 import com.reactnativenavigation.presentation.StackPresenter;
15
+import com.reactnativenavigation.react.EventEmitter;
15
 import com.reactnativenavigation.utils.ImageLoader;
16
 import com.reactnativenavigation.utils.ImageLoader;
16
 import com.reactnativenavigation.utils.UiUtils;
17
 import com.reactnativenavigation.utils.UiUtils;
17
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
18
 import com.reactnativenavigation.viewcontrollers.ChildControllersRegistry;
33
                 return topBar;
34
                 return topBar;
34
             }
35
             }
35
         };
36
         };
36
-        return new StackControllerBuilder(activity)
37
+        return new StackControllerBuilder(activity, Mockito.mock(EventEmitter.class))
37
                 .setId("stack")
38
                 .setId("stack")
38
                 .setChildRegistry(new ChildControllersRegistry())
39
                 .setChildRegistry(new ChildControllersRegistry())
39
                 .setTopBarController(topBarController)
40
                 .setTopBarController(topBarController)

+ 1
- 14
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java 查看文件

11
 import com.reactnativenavigation.TestUtils;
11
 import com.reactnativenavigation.TestUtils;
12
 import com.reactnativenavigation.mocks.TestComponentLayout;
12
 import com.reactnativenavigation.mocks.TestComponentLayout;
13
 import com.reactnativenavigation.mocks.TestReactView;
13
 import com.reactnativenavigation.mocks.TestReactView;
14
-import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
15
-import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
16
-import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
17
 import com.reactnativenavigation.parse.Options;
14
 import com.reactnativenavigation.parse.Options;
18
 import com.reactnativenavigation.parse.params.Bool;
15
 import com.reactnativenavigation.parse.params.Bool;
19
 import com.reactnativenavigation.parse.params.Colour;
16
 import com.reactnativenavigation.parse.params.Colour;
20
 import com.reactnativenavigation.parse.params.Text;
17
 import com.reactnativenavigation.parse.params.Text;
21
 import com.reactnativenavigation.presentation.ComponentPresenter;
18
 import com.reactnativenavigation.presentation.ComponentPresenter;
22
 import com.reactnativenavigation.presentation.Presenter;
19
 import com.reactnativenavigation.presentation.Presenter;
23
-import com.reactnativenavigation.presentation.RenderChecker;
24
-import com.reactnativenavigation.presentation.StackPresenter;
25
 import com.reactnativenavigation.utils.CommandListenerAdapter;
20
 import com.reactnativenavigation.utils.CommandListenerAdapter;
26
-import com.reactnativenavigation.utils.ImageLoader;
27
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
21
 import com.reactnativenavigation.viewcontrollers.stack.StackController;
28
-import com.reactnativenavigation.viewcontrollers.stack.StackControllerBuilder;
29
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
22
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
30
 import com.reactnativenavigation.views.StackLayout;
23
 import com.reactnativenavigation.views.StackLayout;
31
 import com.reactnativenavigation.views.topbar.TopBar;
24
 import com.reactnativenavigation.views.topbar.TopBar;
95
     @Test
88
     @Test
96
     public void initialOptionsAppliedOnAppear() {
89
     public void initialOptionsAppliedOnAppear() {
97
         uut.options.topBar.title.text = new Text("the title");
90
         uut.options.topBar.title.text = new Text("the title");
98
-        StackController stackController =
99
-                new StackControllerBuilder(activity)
100
-                        .setTopBarController(new TopBarController())
101
-                        .setId("stackId")
102
-                        .setInitialOptions(new Options())
103
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new RenderChecker(), new Options()))
104
-                        .build();
91
+        StackController stackController = TestUtils.newStackController(activity).build();
105
         stackController.ensureViewIsCreated();
92
         stackController.ensureViewIsCreated();
106
         stackController.push(uut, new CommandListenerAdapter());
93
         stackController.push(uut, new CommandListenerAdapter());
107
         assertThat(stackController.getTopBar().getTitle()).isEmpty();
94
         assertThat(stackController.getTopBar().getTitle()).isEmpty();

+ 37
- 17
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.java 查看文件

22
 import com.reactnativenavigation.parse.params.Text;
22
 import com.reactnativenavigation.parse.params.Text;
23
 import com.reactnativenavigation.presentation.RenderChecker;
23
 import com.reactnativenavigation.presentation.RenderChecker;
24
 import com.reactnativenavigation.presentation.StackPresenter;
24
 import com.reactnativenavigation.presentation.StackPresenter;
25
+import com.reactnativenavigation.react.EventEmitter;
25
 import com.reactnativenavigation.utils.CommandListenerAdapter;
26
 import com.reactnativenavigation.utils.CommandListenerAdapter;
26
-import com.reactnativenavigation.utils.ImageLoader;
27
 import com.reactnativenavigation.utils.StatusBarUtils;
27
 import com.reactnativenavigation.utils.StatusBarUtils;
28
 import com.reactnativenavigation.utils.TitleBarHelper;
28
 import com.reactnativenavigation.utils.TitleBarHelper;
29
 import com.reactnativenavigation.utils.UiUtils;
29
 import com.reactnativenavigation.utils.UiUtils;
66
 import static org.mockito.Mockito.spy;
66
 import static org.mockito.Mockito.spy;
67
 import static org.mockito.Mockito.times;
67
 import static org.mockito.Mockito.times;
68
 import static org.mockito.Mockito.verify;
68
 import static org.mockito.Mockito.verify;
69
+import static org.mockito.Mockito.verifyNoMoreInteractions;
69
 import static org.mockito.Mockito.when;
70
 import static org.mockito.Mockito.when;
70
 
71
 
71
 @LooperMode(LooperMode.Mode.PAUSED)
72
 @LooperMode(LooperMode.Mode.PAUSED)
83
     private TopBarController topBarController;
84
     private TopBarController topBarController;
84
     private StackPresenter presenter;
85
     private StackPresenter presenter;
85
     private BackButtonHelper backButtonHelper;
86
     private BackButtonHelper backButtonHelper;
87
+    private EventEmitter eventEmitter;
86
 
88
 
87
     @Override
89
     @Override
88
     public void beforeEach() {
90
     public void beforeEach() {
89
         super.beforeEach();
91
         super.beforeEach();
92
+        eventEmitter = Mockito.mock(EventEmitter.class);
90
         backButtonHelper = spy(new BackButtonHelper());
93
         backButtonHelper = spy(new BackButtonHelper());
91
         activity = newActivity();
94
         activity = newActivity();
92
         StatusBarUtils.saveStatusBarHeight(63);
95
         StatusBarUtils.saveStatusBarHeight(63);
383
     @Test
386
     @Test
384
     public void pop_layoutHandlesChildWillDisappear() {
387
     public void pop_layoutHandlesChildWillDisappear() {
385
         TopBarController topBarController = new TopBarController();
388
         TopBarController topBarController = new TopBarController();
386
-        uut = new StackControllerBuilder(activity)
387
-                        .setTopBarController(topBarController)
388
-                        .setId("uut")
389
-                        .setInitialOptions(new Options())
390
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TopBarButtonCreatorMock(), new ImageLoader(), new RenderChecker(), new Options()))
391
-                        .build();
389
+        uut = TestUtils.newStackController(activity)
390
+                .setTopBarController(topBarController)
391
+                .setId("uut")
392
+                .build();
392
         uut.ensureViewIsCreated();
393
         uut.ensureViewIsCreated();
393
         uut.push(child1, new CommandListenerAdapter());
394
         uut.push(child1, new CommandListenerAdapter());
394
         uut.push(child2, new CommandListenerAdapter() {
395
         uut.push(child2, new CommandListenerAdapter() {
404
         });
405
         });
405
     }
406
     }
406
 
407
 
408
+    @Test
409
+    public void pop_popEventIsEmitted() {
410
+        disablePushAnimation(child1, child2);
411
+        disablePopAnimation(child2);
412
+        uut.push(child1, new CommandListenerAdapter());
413
+        uut.push(child2, new CommandListenerAdapter());
414
+
415
+        uut.pop(Options.EMPTY, new CommandListenerAdapter());
416
+        verify(eventEmitter).emitScreenPoppedEvent(child2.getId());
417
+    }
418
+
419
+    @Test
420
+    public void popToRoot_popEventIsEmitted() {
421
+        disablePushAnimation(child1, child2, child3);
422
+        disablePopAnimation(child2, child3);
423
+        uut.push(child1, new CommandListenerAdapter());
424
+        uut.push(child2, new CommandListenerAdapter());
425
+        uut.push(child3, new CommandListenerAdapter());
426
+
427
+        uut.pop(Options.EMPTY, new CommandListenerAdapter());
428
+        verify(eventEmitter).emitScreenPoppedEvent(child3.getId());
429
+        verifyNoMoreInteractions(eventEmitter);
430
+    }
431
+
407
     @Test
432
     @Test
408
     public void stackOperations() {
433
     public void stackOperations() {
409
         assertThat(uut.peek()).isNull();
434
         assertThat(uut.peek()).isNull();
957
 
982
 
958
     @Test
983
     @Test
959
     public void mergeChildOptions_updatesViewWithNewOptions() {
984
     public void mergeChildOptions_updatesViewWithNewOptions() {
960
-        StackController uut = spy(new StackControllerBuilder(activity)
961
-                        .setTopBarController(new TopBarController())
962
-                        .setId("stack")
963
-                        .setInitialOptions(new Options())
964
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TitleBarReactViewCreatorMock(), ImageLoaderMock.mock(), new RenderChecker(), Options.EMPTY))
965
-                        .build());
985
+        StackController uut = spy(TestUtils.newStackController(activity)
986
+                .setId("stack")
987
+                .build());
966
         Options optionsToMerge = new Options();
988
         Options optionsToMerge = new Options();
967
         ViewController vc = mock(ViewController.class);
989
         ViewController vc = mock(ViewController.class);
968
         uut.mergeChildOptions(optionsToMerge, vc);
990
         uut.mergeChildOptions(optionsToMerge, vc);
971
 
993
 
972
     @Test
994
     @Test
973
     public void mergeChildOptions_updatesParentControllerWithNewOptions() {
995
     public void mergeChildOptions_updatesParentControllerWithNewOptions() {
974
-        StackController uut = new StackControllerBuilder(activity)
975
-                        .setTopBarController(new TopBarController())
996
+        StackController uut = TestUtils.newStackController(activity)
976
                         .setId("stack")
997
                         .setId("stack")
977
-                        .setInitialOptions(new Options())
978
-                        .setStackPresenter(new StackPresenter(activity, new TitleBarReactViewCreatorMock(), new TopBarBackgroundViewCreatorMock(), new TitleBarReactViewCreatorMock(), ImageLoaderMock.mock(), new RenderChecker(), Options.EMPTY))
979
                         .build();
998
                         .build();
980
         ParentController parentController = Mockito.mock(ParentController.class);
999
         ParentController parentController = Mockito.mock(ParentController.class);
981
         uut.setParentController(parentController);
1000
         uut.setParentController(parentController);
1136
     private StackControllerBuilder createStackBuilder(String id, List<ViewController> children) {
1155
     private StackControllerBuilder createStackBuilder(String id, List<ViewController> children) {
1137
         createTopBarController();
1156
         createTopBarController();
1138
         return TestUtils.newStackController(activity)
1157
         return TestUtils.newStackController(activity)
1158
+                .setEventEmitter(eventEmitter)
1139
                 .setChildren(children)
1159
                 .setChildren(children)
1140
                 .setId(id)
1160
                 .setId(id)
1141
                 .setTopBarController(topBarController)
1161
                 .setTopBarController(topBarController)

+ 1
- 3
lib/ios/RNNCommandsHandler.m 查看文件

205
 	UINavigationController *nvc = vc.navigationController;
205
 	UINavigationController *nvc = vc.navigationController;
206
 	
206
 	
207
 	if ([nvc topViewController] == vc) {
207
 	if ([nvc topViewController] == vc) {
208
-		if (vc.resolveOptionsWithDefault.animations.pop) {
208
+		if (vc.resolveOptions.animations.pop.hasCustomAnimation) {
209
 			nvc.delegate = vc;
209
 			nvc.delegate = vc;
210
-		} else {
211
-			nvc.delegate = nil;
212
 		}
210
 		}
213
 	} else {
211
 	} else {
214
 		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;
212
 		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;

+ 0
- 2
lib/ios/RNNComponentViewController.m 查看文件

11
 	
11
 	
12
 	self.animator = [[RNNAnimator alloc] initWithTransitionOptions:self.resolveOptions.customTransition];
12
 	self.animator = [[RNNAnimator alloc] initWithTransitionOptions:self.resolveOptions.customTransition];
13
 	
13
 	
14
-	self.navigationController.delegate = self;
15
-	
16
 	return self;
14
 	return self;
17
 }
15
 }
18
 
16
 

+ 2
- 1
lib/ios/RNNEventEmitter.h 查看文件

1
-
2
 #import <Foundation/Foundation.h>
1
 #import <Foundation/Foundation.h>
3
 
2
 
4
 #import <React/RCTEventEmitter.h>
3
 #import <React/RCTEventEmitter.h>
26
 
25
 
27
 - (void)sendModalsDismissedEvent:(NSString *)componentId numberOfModalsDismissed:(NSNumber *)modalsDismissed;
26
 - (void)sendModalsDismissedEvent:(NSString *)componentId numberOfModalsDismissed:(NSNumber *)modalsDismissed;
28
 
27
 
28
+- (void)sendScreenPoppedEvent:(NSString *)componentId;
29
+
29
 
30
 
30
 @end
31
 @end

+ 87
- 79
lib/ios/RNNEventEmitter.m 查看文件

2
 #import "RNNUtils.h"
2
 #import "RNNUtils.h"
3
 
3
 
4
 @implementation RNNEventEmitter {
4
 @implementation RNNEventEmitter {
5
-	NSInteger _appLaunchedListenerCount;
6
-	BOOL _appLaunchedEventDeferred;
5
+    NSInteger _appLaunchedListenerCount;
6
+    BOOL _appLaunchedEventDeferred;
7
 }
7
 }
8
 
8
 
9
 RCT_EXPORT_MODULE();
9
 RCT_EXPORT_MODULE();
18
 static NSString* const SearchBarUpdated 		= @"RNN.SearchBarUpdated";
18
 static NSString* const SearchBarUpdated 		= @"RNN.SearchBarUpdated";
19
 static NSString* const SearchBarCancelPressed 	= @"RNN.SearchBarCancelPressed";
19
 static NSString* const SearchBarCancelPressed 	= @"RNN.SearchBarCancelPressed";
20
 static NSString* const PreviewCompleted         = @"RNN.PreviewCompleted";
20
 static NSString* const PreviewCompleted         = @"RNN.PreviewCompleted";
21
-
22
--(NSArray<NSString *> *)supportedEvents {
23
-	return @[AppLaunched,
24
-			 CommandCompleted,
25
-			 BottomTabSelected,
26
-			 ComponentDidAppear,
27
-			 ComponentDidDisappear,
28
-			 NavigationButtonPressed,
29
-			 ModalDismissed,
30
-			 SearchBarUpdated,
31
-			 SearchBarCancelPressed,
32
-			 PreviewCompleted];
21
+static NSString* const ScreenPopped             = @"RNN.ScreenPopped";
22
+
23
+- (NSArray<NSString *> *)supportedEvents {
24
+    return @[AppLaunched,
25
+             CommandCompleted,
26
+             BottomTabSelected,
27
+             ComponentDidAppear,
28
+             ComponentDidDisappear,
29
+             NavigationButtonPressed,
30
+             ModalDismissed,
31
+             SearchBarUpdated,
32
+             SearchBarCancelPressed,
33
+             PreviewCompleted,
34
+             ScreenPopped];
33
 }
35
 }
34
 
36
 
35
 # pragma mark public
37
 # pragma mark public
36
 
38
 
37
--(void)sendOnAppLaunched {
38
-	if (_appLaunchedListenerCount > 0) {
39
-		[self send:AppLaunched body:nil];
40
-	} else {
41
-		_appLaunchedEventDeferred = TRUE;
42
-	}
39
+- (void)sendOnAppLaunched {
40
+    if (_appLaunchedListenerCount > 0) {
41
+        [self send:AppLaunched body:nil];
42
+    } else {
43
+        _appLaunchedEventDeferred = TRUE;
44
+    }
43
 }
45
 }
44
 
46
 
45
--(void)sendComponentDidAppear:(NSString *)componentId componentName:(NSString *)componentName {
46
-	[self send:ComponentDidAppear body:@{
47
-										 @"componentId":componentId,
48
-										 @"componentName": componentName
49
-										 }];
47
+- (void)sendComponentDidAppear:(NSString *)componentId componentName:(NSString *)componentName {
48
+    [self send:ComponentDidAppear body:@{
49
+        @"componentId":componentId,
50
+        @"componentName": componentName
51
+    }];
50
 }
52
 }
51
 
53
 
52
--(void)sendComponentDidDisappear:(NSString *)componentId componentName:(NSString *)componentName{
53
-	[self send:ComponentDidDisappear body:@{
54
-											@"componentId":componentId,
55
-											@"componentName": componentName
56
-											}];
54
+- (void)sendComponentDidDisappear:(NSString *)componentId componentName:(NSString *)componentName{
55
+    [self send:ComponentDidDisappear body:@{
56
+        @"componentId":componentId,
57
+        @"componentName": componentName
58
+    }];
57
 }
59
 }
58
 
60
 
59
--(void)sendOnNavigationButtonPressed:(NSString *)componentId buttonId:(NSString*)buttonId {
60
-	[self send:NavigationButtonPressed body:@{
61
-											  @"componentId": componentId,
62
-											  @"buttonId": buttonId
63
-											  }];
61
+- (void)sendOnNavigationButtonPressed:(NSString *)componentId buttonId:(NSString*)buttonId {
62
+    [self send:NavigationButtonPressed body:@{
63
+        @"componentId": componentId,
64
+        @"buttonId": buttonId
65
+    }];
64
 }
66
 }
65
 
67
 
66
--(void)sendBottomTabSelected:(NSNumber *)selectedTabIndex unselected:(NSNumber*)unselectedTabIndex {
67
-	[self send:BottomTabSelected body:@{
68
-									  @"selectedTabIndex": selectedTabIndex,
69
-									  @"unselectedTabIndex": unselectedTabIndex
70
-									  }];
68
+- (void)sendBottomTabSelected:(NSNumber *)selectedTabIndex unselected:(NSNumber*)unselectedTabIndex {
69
+    [self send:BottomTabSelected body:@{
70
+        @"selectedTabIndex": selectedTabIndex,
71
+        @"unselectedTabIndex": unselectedTabIndex
72
+    }];
71
 }
73
 }
72
 
74
 
73
--(void)sendOnNavigationCommandCompletion:(NSString *)commandName commandId:(NSString *)commandId params:(NSDictionary*)params {
74
-	[self send:CommandCompleted body:@{
75
-									   @"commandId":commandId,
76
-									   @"commandName":commandName,
77
-									   @"params": params,
78
-									   @"completionTime": [RNNUtils getCurrentTimestamp]
79
-									   }];
75
+- (void)sendOnNavigationCommandCompletion:(NSString *)commandName commandId:(NSString *)commandId params:(NSDictionary*)params {
76
+    [self send:CommandCompleted body:@{
77
+        @"commandId":commandId,
78
+        @"commandName":commandName,
79
+        @"params": params,
80
+        @"completionTime": [RNNUtils getCurrentTimestamp]
81
+    }];
80
 }
82
 }
81
 
83
 
82
--(void)sendOnSearchBarUpdated:(NSString *)componentId
83
-						 text:(NSString*)text
84
-					isFocused:(BOOL)isFocused {
85
-	[self send:SearchBarUpdated body:@{
86
-									   @"componentId": componentId,
87
-									   @"text": text,
88
-									   @"isFocused": @(isFocused)
89
-									   }];
84
+- (void)sendOnSearchBarUpdated:(NSString *)componentId
85
+                          text:(NSString*)text
86
+                     isFocused:(BOOL)isFocused {
87
+    [self send:SearchBarUpdated body:@{
88
+        @"componentId": componentId,
89
+        @"text": text,
90
+        @"isFocused": @(isFocused)
91
+    }];
90
 }
92
 }
91
 
93
 
92
 - (void)sendOnSearchBarCancelPressed:(NSString *)componentId {
94
 - (void)sendOnSearchBarCancelPressed:(NSString *)componentId {
93
-	[self send:SearchBarCancelPressed body:@{
94
-											@"componentId": componentId
95
-											}];
95
+    [self send:SearchBarCancelPressed body:@{
96
+        @"componentId": componentId
97
+    }];
96
 }
98
 }
97
 
99
 
98
 - (void)sendOnPreviewCompleted:(NSString *)componentId previewComponentId:(NSString *)previewComponentId {
100
 - (void)sendOnPreviewCompleted:(NSString *)componentId previewComponentId:(NSString *)previewComponentId {
99
-	[self send:PreviewCompleted body:@{
100
-											 @"componentId": componentId,
101
-											 @"previewComponentId": previewComponentId
102
-                       }];
101
+    [self send:PreviewCompleted body:@{
102
+        @"componentId": componentId,
103
+        @"previewComponentId": previewComponentId
104
+    }];
103
 }
105
 }
104
 
106
 
105
 - (void)sendModalsDismissedEvent:(NSString *)componentId numberOfModalsDismissed:(NSNumber *)modalsDismissed {
107
 - (void)sendModalsDismissedEvent:(NSString *)componentId numberOfModalsDismissed:(NSNumber *)modalsDismissed {
106
-	[self send:ModalDismissed body:@{
107
-											 @"componentId": componentId,
108
-											 @"modalsDismissed": modalsDismissed
109
-											 }];
108
+    [self send:ModalDismissed body:@{
109
+        @"componentId": componentId,
110
+        @"modalsDismissed": modalsDismissed
111
+    }];
112
+}
113
+
114
+- (void)sendScreenPoppedEvent:(NSString *)componentId {
115
+    [self send:ScreenPopped body:@{
116
+        @"componentId": componentId
117
+    }];
110
 }
118
 }
111
 
119
 
112
 - (void)addListener:(NSString *)eventName {
120
 - (void)addListener:(NSString *)eventName {
113
-	[super addListener:eventName];
114
-	if ([eventName isEqualToString:AppLaunched]) {
115
-		_appLaunchedListenerCount++;
116
-		if (_appLaunchedEventDeferred) {
117
-			_appLaunchedEventDeferred = FALSE;
118
-			[self sendOnAppLaunched];
119
-		}
120
-	}
121
+    [super addListener:eventName];
122
+    if ([eventName isEqualToString:AppLaunched]) {
123
+        _appLaunchedListenerCount++;
124
+        if (_appLaunchedEventDeferred) {
125
+            _appLaunchedEventDeferred = FALSE;
126
+            [self sendOnAppLaunched];
127
+        }
128
+    }
121
 }
129
 }
122
 
130
 
123
 # pragma mark private
131
 # pragma mark private
124
 
132
 
125
--(void)send:(NSString *)eventName body:(id)body {
126
-	if (self.bridge == nil) {
127
-		return;
128
-	}
129
-	[self sendEventWithName:eventName body:body];
133
+- (void)send:(NSString *)eventName body:(id)body {
134
+    if (self.bridge == nil) {
135
+        return;
136
+    }
137
+    [self sendEventWithName:eventName body:body];
130
 }
138
 }
131
 
139
 
132
 @end
140
 @end

+ 0
- 2
lib/ios/RNNNavigationStackManager.m 查看文件

19
 
19
 
20
 	if (animationDelegate) {
20
 	if (animationDelegate) {
21
 		nvc.delegate = animationDelegate;
21
 		nvc.delegate = animationDelegate;
22
-	} else {
23
-		nvc.delegate = nil;
24
 	}
22
 	}
25
 	
23
 	
26
 	[self performAnimationBlock:^{
24
 	[self performAnimationBlock:^{

+ 33
- 10
lib/ios/RNNStackController.m 查看文件

1
 #import "RNNStackController.h"
1
 #import "RNNStackController.h"
2
 #import "RNNComponentViewController.h"
2
 #import "RNNComponentViewController.h"
3
 
3
 
4
-@implementation RNNStackController
4
+@implementation RNNStackController {
5
+    UIViewController* _presentedViewController;
6
+}
7
+
8
+- (instancetype)init {
9
+    self = [super init];
10
+    self.delegate = self;
11
+    return self;
12
+}
5
 
13
 
6
--(void)setDefaultOptions:(RNNNavigationOptions *)defaultOptions {
14
+- (void)setDefaultOptions:(RNNNavigationOptions *)defaultOptions {
7
 	[super setDefaultOptions:defaultOptions];
15
 	[super setDefaultOptions:defaultOptions];
8
 	[self.presenter setDefaultOptions:defaultOptions];
16
 	[self.presenter setDefaultOptions:defaultOptions];
9
 }
17
 }
30
 }
38
 }
31
 
39
 
32
 - (UIViewController *)popViewControllerAnimated:(BOOL)animated {
40
 - (UIViewController *)popViewControllerAnimated:(BOOL)animated {
33
-	if (self.viewControllers.count > 1) {
34
-		UIViewController *controller = self.viewControllers[self.viewControllers.count - 2];
35
-		if ([controller isKindOfClass:[RNNComponentViewController class]]) {
36
-			RNNComponentViewController *rnnController = (RNNComponentViewController *)controller;
37
-			[self.presenter applyOptionsBeforePopping:rnnController.resolveOptions];
38
-		}
39
-	}
40
-	
41
+    [self prepareForPop];
41
 	return [super popViewControllerAnimated:animated];
42
 	return [super popViewControllerAnimated:animated];
42
 }
43
 }
43
 
44
 
45
+- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
46
+    if ([self.viewControllers indexOfObject:_presentedViewController] < 0) {
47
+        [self sendScreenPoppedEvent:_presentedViewController];
48
+    }
49
+    
50
+    _presentedViewController = viewController;
51
+}
52
+
53
+- (void)sendScreenPoppedEvent:(UIViewController *)poppedScreen {
54
+    [self.eventEmitter sendScreenPoppedEvent:poppedScreen.layoutInfo.componentId];
55
+}
56
+
57
+- (void)prepareForPop {
58
+    if (self.viewControllers.count > 1) {
59
+        UIViewController *controller = self.viewControllers[self.viewControllers.count - 2];
60
+        if ([controller isKindOfClass:[RNNComponentViewController class]]) {
61
+            RNNComponentViewController *rnnController = (RNNComponentViewController *)controller;
62
+            [self.presenter applyOptionsBeforePopping:rnnController.resolveOptions];
63
+        }
64
+    }
65
+}
66
+
44
 - (UIViewController *)childViewControllerForStatusBarStyle {
67
 - (UIViewController *)childViewControllerForStatusBarStyle {
45
 	return self.topViewController;
68
 	return self.topViewController;
46
 }
69
 }

+ 6
- 1
lib/src/adapters/NativeEventsReceiver.ts 查看文件

6
   SearchBarUpdatedEvent,
6
   SearchBarUpdatedEvent,
7
   SearchBarCancelPressedEvent,
7
   SearchBarCancelPressedEvent,
8
   PreviewCompletedEvent,
8
   PreviewCompletedEvent,
9
-  ModalDismissedEvent
9
+  ModalDismissedEvent,
10
+  ScreenPoppedEvent
10
 } from '../interfaces/ComponentEvents';
11
 } from '../interfaces/ComponentEvents';
11
 import { CommandCompletedEvent, BottomTabSelectedEvent } from '../interfaces/Events';
12
 import { CommandCompletedEvent, BottomTabSelectedEvent } from '../interfaces/Events';
12
 
13
 
67
   public registerBottomTabSelectedListener(callback: (data: BottomTabSelectedEvent) => void): EmitterSubscription {
68
   public registerBottomTabSelectedListener(callback: (data: BottomTabSelectedEvent) => void): EmitterSubscription {
68
     return this.emitter.addListener('RNN.BottomTabSelected', callback);
69
     return this.emitter.addListener('RNN.BottomTabSelected', callback);
69
   }
70
   }
71
+
72
+  public registerScreenPoppedListener(callback: (event: ScreenPoppedEvent) => void): EmitterSubscription {
73
+    return this.emitter.addListener('RNN.ScreenPopped', callback);
74
+  }
70
 }
75
 }

+ 13
- 0
lib/src/events/ComponentEventsObserver.test.tsx 查看文件

18
   const searchBarCancelPressedFn = jest.fn();
18
   const searchBarCancelPressedFn = jest.fn();
19
   const previewCompletedFn = jest.fn();
19
   const previewCompletedFn = jest.fn();
20
   const modalDismissedFn = jest.fn();
20
   const modalDismissedFn = jest.fn();
21
+  const screenPoppedFn = jest.fn();
21
   let subscription: EventSubscription;
22
   let subscription: EventSubscription;
22
   let uut: ComponentEventsObserver;
23
   let uut: ComponentEventsObserver;
23
 
24
 
68
       previewCompletedFn(event);
69
       previewCompletedFn(event);
69
     }
70
     }
70
 
71
 
72
+    screenPopped(event: any) {
73
+      screenPoppedFn(event);
74
+    }
75
+
71
     render() {
76
     render() {
72
       return 'Hello';
77
       return 'Hello';
73
     }
78
     }
115
       previewCompletedFn(event);
120
       previewCompletedFn(event);
116
     }
121
     }
117
 
122
 
123
+    screenPopped(event: any) {
124
+      screenPoppedFn(event);
125
+    }
126
+
118
     render() {
127
     render() {
119
       return 'Hello';
128
       return 'Hello';
120
     }
129
     }
191
     expect(previewCompletedFn).toHaveBeenCalledTimes(1);
200
     expect(previewCompletedFn).toHaveBeenCalledTimes(1);
192
     expect(previewCompletedFn).toHaveBeenCalledWith({ componentId: 'myCompId' });
201
     expect(previewCompletedFn).toHaveBeenCalledWith({ componentId: 'myCompId' });
193
 
202
 
203
+    uut.notifyScreenPopped({ componentId: 'myCompId' });
204
+    expect(screenPoppedFn).toHaveBeenCalledTimes(1);
205
+    expect(screenPoppedFn).toHaveBeenLastCalledWith({ componentId: 'myCompId' })
206
+
194
     tree.unmount();
207
     tree.unmount();
195
     expect(willUnmountFn).toHaveBeenCalledTimes(1);
208
     expect(willUnmountFn).toHaveBeenCalledTimes(1);
196
   });
209
   });

+ 8
- 1
lib/src/events/ComponentEventsObserver.ts 查看文件

12
   SearchBarCancelPressedEvent,
12
   SearchBarCancelPressedEvent,
13
   ComponentEvent,
13
   ComponentEvent,
14
   PreviewCompletedEvent,
14
   PreviewCompletedEvent,
15
-  ModalDismissedEvent
15
+  ModalDismissedEvent,
16
+  ScreenPoppedEvent
16
 } from '../interfaces/ComponentEvents';
17
 } from '../interfaces/ComponentEvents';
17
 import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
18
 import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
18
 import { Store } from '../components/Store';
19
 import { Store } from '../components/Store';
34
     this.notifySearchBarUpdated = this.notifySearchBarUpdated.bind(this);
35
     this.notifySearchBarUpdated = this.notifySearchBarUpdated.bind(this);
35
     this.notifySearchBarCancelPressed = this.notifySearchBarCancelPressed.bind(this);
36
     this.notifySearchBarCancelPressed = this.notifySearchBarCancelPressed.bind(this);
36
     this.notifyPreviewCompleted = this.notifyPreviewCompleted.bind(this);
37
     this.notifyPreviewCompleted = this.notifyPreviewCompleted.bind(this);
38
+    this.notifyScreenPopped = this.notifyScreenPopped.bind(this);
37
   }
39
   }
38
 
40
 
39
   public registerOnceForAllComponentEvents() {
41
   public registerOnceForAllComponentEvents() {
46
     this.nativeEventsReceiver.registerSearchBarUpdatedListener(this.notifySearchBarUpdated);
48
     this.nativeEventsReceiver.registerSearchBarUpdatedListener(this.notifySearchBarUpdated);
47
     this.nativeEventsReceiver.registerSearchBarCancelPressedListener(this.notifySearchBarCancelPressed);
49
     this.nativeEventsReceiver.registerSearchBarCancelPressedListener(this.notifySearchBarCancelPressed);
48
     this.nativeEventsReceiver.registerPreviewCompletedListener(this.notifyPreviewCompleted);
50
     this.nativeEventsReceiver.registerPreviewCompletedListener(this.notifyPreviewCompleted);
51
+    this.nativeEventsReceiver.registerScreenPoppedListener(this.notifyPreviewCompleted);
49
   }
52
   }
50
 
53
 
51
   public bindComponent(component: React.Component<any>, componentId?: string): EventSubscription {
54
   public bindComponent(component: React.Component<any>, componentId?: string): EventSubscription {
96
     this.triggerOnAllListenersByComponentId(event, 'previewCompleted');
99
     this.triggerOnAllListenersByComponentId(event, 'previewCompleted');
97
   }
100
   }
98
 
101
 
102
+  notifyScreenPopped(event: ScreenPoppedEvent) {
103
+    this.triggerOnAllListenersByComponentId(event, 'screenPopped');
104
+  }
105
+
99
   private triggerOnAllListenersByComponentId(event: ComponentEvent, method: string) {
106
   private triggerOnAllListenersByComponentId(event: ComponentEvent, method: string) {
100
     forEach(this.listeners[event.componentId], (component) => {
107
     forEach(this.listeners[event.componentId], (component) => {
101
       if (component && component[method]) {
108
       if (component && component[method]) {

+ 7
- 0
lib/src/events/EventsRegistry.test.tsx 查看文件

112
     mockScreenEventsRegistry.bindComponent.mockReturnValueOnce(subscription);
112
     mockScreenEventsRegistry.bindComponent.mockReturnValueOnce(subscription);
113
     expect(uut.bindComponent({} as React.Component<any>)).toEqual(subscription);
113
     expect(uut.bindComponent({} as React.Component<any>)).toEqual(subscription);
114
   });
114
   });
115
+
116
+  it('delegates screenPopped to nativeEventsReceiver', () => {
117
+    const cb = jest.fn();
118
+    uut.registerScreenPoppedListener(cb);
119
+    expect(mockNativeEventsReceiver.registerScreenPoppedListener).toHaveBeenCalledTimes(1);
120
+    expect(mockNativeEventsReceiver.registerScreenPoppedListener).toHaveBeenCalledWith(cb);
121
+  });
115
 });
122
 });

+ 7
- 1
lib/src/events/EventsRegistry.ts 查看文件

11
   SearchBarUpdatedEvent,
11
   SearchBarUpdatedEvent,
12
   SearchBarCancelPressedEvent,
12
   SearchBarCancelPressedEvent,
13
   PreviewCompletedEvent,
13
   PreviewCompletedEvent,
14
-  ModalDismissedEvent
14
+  ModalDismissedEvent,
15
+  ScreenPoppedEvent
15
 } from '../interfaces/ComponentEvents';
16
 } from '../interfaces/ComponentEvents';
16
 import { CommandCompletedEvent, BottomTabSelectedEvent } from '../interfaces/Events';
17
 import { CommandCompletedEvent, BottomTabSelectedEvent } from '../interfaces/Events';
17
 
18
 
65
   public bindComponent(component: React.Component<any>, componentId?: string): EventSubscription {
66
   public bindComponent(component: React.Component<any>, componentId?: string): EventSubscription {
66
     return this.componentEventsObserver.bindComponent(component, componentId);
67
     return this.componentEventsObserver.bindComponent(component, componentId);
67
   }
68
   }
69
+
70
+  public registerScreenPoppedListener(callback: (event: ScreenPoppedEvent) => void): EmitterSubscription {
71
+    return this.nativeEventsReceiver.registerScreenPoppedListener(callback);
72
+  }
73
+
68
 }
74
 }

+ 4
- 0
lib/src/interfaces/ComponentEvents.ts 查看文件

33
   componentName?: string;
33
   componentName?: string;
34
   previewComponentId?: string;
34
   previewComponentId?: string;
35
 }
35
 }
36
+
37
+export interface ScreenPoppedEvent extends ComponentEvent {
38
+  componentId: string;
39
+}

+ 12
- 3
playground/ios/NavigationTests/RNNNavigationControllerTest.m 查看文件

17
 	UIViewController* _vc3;
17
 	UIViewController* _vc3;
18
 	RNNNavigationOptions* _options;
18
 	RNNNavigationOptions* _options;
19
 	RNNTestRootViewCreator* _creator;
19
 	RNNTestRootViewCreator* _creator;
20
+	RNNEventEmitter* _eventEmitter;
20
 }
21
 }
21
 
22
 
22
 - (void)setUp {
23
 - (void)setUp {
23
     [super setUp];
24
     [super setUp];
25
+	_eventEmitter = [OCMockObject niceMockForClass:[RNNEventEmitter class]];
24
 	_creator = [[RNNTestRootViewCreator alloc] init];
26
 	_creator = [[RNNTestRootViewCreator alloc] init];
25
 	_vc1 = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[OCMockObject partialMockForObject:[[RNNComponentPresenter alloc] init]] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
27
 	_vc1 = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[OCMockObject partialMockForObject:[[RNNComponentPresenter alloc] init]] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
26
 	_vc2 = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNComponentPresenter alloc] init] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
28
 	_vc2 = [[RNNComponentViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNComponentPresenter alloc] init] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
27
 	_vc2Mock = [OCMockObject partialMockForObject:_vc2];
29
 	_vc2Mock = [OCMockObject partialMockForObject:_vc2];
28
 	_vc3 = [UIViewController new];
30
 	_vc3 = [UIViewController new];
29
 	_options = [OCMockObject partialMockForObject:[[RNNNavigationOptions alloc] initEmptyOptions]];
31
 	_options = [OCMockObject partialMockForObject:[[RNNNavigationOptions alloc] initEmptyOptions]];
30
-	self.uut = [[RNNStackController alloc] initWithLayoutInfo:nil creator:_creator options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNStackPresenter alloc] init]] eventEmitter:nil childViewControllers:@[_vc1, _vc2]];
32
+	self.uut = [[RNNStackController alloc] initWithLayoutInfo:nil creator:_creator options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNStackPresenter alloc] init]] eventEmitter:_eventEmitter childViewControllers:@[_vc1, _vc2]];
31
 }
33
 }
32
 
34
 
33
 - (void)testInitWithLayoutInfo_shouldBindPresenter {
35
 - (void)testInitWithLayoutInfo_shouldBindPresenter {
139
 	[uut setViewControllers:@[_vc1, _vc2]];
141
 	[uut setViewControllers:@[_vc1, _vc2]];
140
 	
142
 	
141
 	[[(id)uut.presenter expect] applyOptionsBeforePopping:[OCMArg any]];
143
 	[[(id)uut.presenter expect] applyOptionsBeforePopping:[OCMArg any]];
142
-	
143
 	[uut popViewControllerAnimated:NO];
144
 	[uut popViewControllerAnimated:NO];
144
-	
145
 	[(id)uut.presenter verify];
145
 	[(id)uut.presenter verify];
146
 }
146
 }
147
 
147
 
148
+- (void)testPopViewController_ShouldEmitScreenPoppedEvent {
149
+	RNNStackController* uut = [RNNStackController new];
150
+	[uut setViewControllers:@[_vc1, _vc2]];
151
+	
152
+	[[(id)uut.eventEmitter expect] sendScreenPoppedEvent:_vc2.layoutInfo.componentId];
153
+	[uut popViewControllerAnimated:NO];
154
+	[(id)uut.eventEmitter verify];
155
+}
156
+
148
 - (void)testOverrideOptionsShouldOverrideOptionsState {
157
 - (void)testOverrideOptionsShouldOverrideOptionsState {
149
 	RNNNavigationOptions* overrideOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
158
 	RNNNavigationOptions* overrideOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
150
 	[(RNNNavigationOptions*)[(id)self.uut.options expect] overrideOptions:overrideOptions];
159
 	[(RNNNavigationOptions*)[(id)self.uut.options expect] overrideOptions:overrideOptions];