Browse Source

SetRoot wait for render (#4688)

* Layout changes

* WIP

* Revert "Layout changes"

This reverts commit 4da8cfbd12.

* Clean topBar.component and topBar.background.component when not needed

* Move custom components login from viewController to presenters

* Change waitForRender and enabled property from primitive bool to Bool object

* Fix ios unit tests

* Add unit tests

* Remove RNNReactView observer when view ready

* Load react components from component manager

* Remove createCustomReactView duplication

* Handle render children in each Layout

* Wait for navigation presenter components render, refactor RNNTitleViewHelper

* Fix unit tests

* Add waitForRender for custom react components

* Fixes e2e
Yogev Ben David 5 years ago
parent
commit
5abea28c53
No account linked to committer's email address
52 changed files with 621 additions and 305 deletions
  1. 12
    0
      lib/ios/RNNBasePresenter.m
  2. 5
    1
      lib/ios/RNNBridgeManager.m
  3. 30
    24
      lib/ios/RNNCommandsHandler.m
  4. 1
    0
      lib/ios/RNNComponentOptions.h
  5. 1
    0
      lib/ios/RNNComponentOptions.m
  6. 5
    2
      lib/ios/RNNControllerFactory.h
  7. 10
    17
      lib/ios/RNNControllerFactory.m
  8. 5
    1
      lib/ios/RNNLayoutProtocol.h
  9. 0
    7
      lib/ios/RNNLeafProtocol.h
  10. 2
    2
      lib/ios/RNNModalManager.m
  11. 2
    1
      lib/ios/RNNNavigationButtons.h
  12. 9
    5
      lib/ios/RNNNavigationButtons.m
  13. 3
    0
      lib/ios/RNNNavigationController.h
  14. 26
    1
      lib/ios/RNNNavigationController.m
  15. 6
    0
      lib/ios/RNNNavigationControllerPresenter.h
  16. 98
    0
      lib/ios/RNNNavigationControllerPresenter.m
  17. 2
    1
      lib/ios/RNNParentProtocol.h
  18. 17
    0
      lib/ios/RNNReactComponentManager.h
  19. 52
    0
      lib/ios/RNNReactComponentManager.m
  20. 0
    6
      lib/ios/RNNReactRootView.h
  21. 0
    16
      lib/ios/RNNReactRootView.m
  22. 10
    18
      lib/ios/RNNReactRootViewCreator.m
  23. 5
    0
      lib/ios/RNNReactView.h
  24. 13
    5
      lib/ios/RNNReactView.m
  25. 24
    135
      lib/ios/RNNRootViewController.m
  26. 3
    2
      lib/ios/RNNRootViewCreator.h
  27. 2
    2
      lib/ios/RNNScreenTransition.h
  28. 2
    3
      lib/ios/RNNScreenTransition.m
  29. 1
    0
      lib/ios/RNNSideMenuChildVC.h
  30. 4
    0
      lib/ios/RNNSideMenuChildVC.m
  31. 1
    0
      lib/ios/RNNSideMenuController.h
  32. 22
    0
      lib/ios/RNNSideMenuController.m
  33. 20
    0
      lib/ios/RNNSplitViewController.m
  34. 20
    0
      lib/ios/RNNTabBarController.m
  35. 2
    0
      lib/ios/RNNTitleViewHelper.h
  36. 8
    7
      lib/ios/RNNTitleViewHelper.m
  37. 0
    1
      lib/ios/RNNTopTabOptions.m
  38. 1
    1
      lib/ios/RNNTopTabsViewController.h
  39. 21
    0
      lib/ios/RNNTopTabsViewController.m
  40. 2
    0
      lib/ios/RNNTransitionsOptions.h
  41. 2
    0
      lib/ios/RNNTransitionsOptions.m
  42. 5
    2
      lib/ios/RNNViewControllerPresenter.h
  43. 94
    3
      lib/ios/RNNViewControllerPresenter.m
  44. 8
    8
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  45. 38
    11
      lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m
  46. 11
    11
      lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m
  47. 6
    4
      lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m
  48. 5
    5
      lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m
  49. 1
    1
      lib/ios/ReactNativeNavigationTests/RNNTestRootViewCreator.m
  50. 1
    1
      lib/ios/ReactNativeNavigationTests/RNNViewControllerPresenterTest.m
  51. 1
    0
      lib/ios/UIViewController+RNNOptions.m
  52. 2
    1
      playground/src/app.js

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

@@ -1,9 +1,21 @@
1 1
 #import "RNNBasePresenter.h"
2 2
 #import "UIViewController+RNNOptions.h"
3 3
 #import "RNNTabBarItemCreator.h"
4
+#import "RNNReactComponentManager.h"
5
+
6
+@interface RNNBasePresenter ()
7
+@property (nonatomic, strong) RNNReactComponentManager* componentManager;
8
+@end
9
+
4 10
 
5 11
 @implementation RNNBasePresenter
6 12
 
13
+- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager {
14
+	self = [super init];
15
+	self.componentManager = componentManager;
16
+	return self;
17
+}
18
+
7 19
 - (void)bindViewController:(UIViewController *)bindedViewController {
8 20
 	_bindedViewController = bindedViewController;
9 21
 }

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

@@ -8,11 +8,13 @@
8 8
 #import "RNNBridgeModule.h"
9 9
 #import "RNNRootViewCreator.h"
10 10
 #import "RNNReactRootViewCreator.h"
11
+#import "RNNReactComponentManager.h"
11 12
 
12 13
 @interface RNNBridgeManager() <RCTBridgeDelegate>
13 14
 
14 15
 @property (nonatomic, strong, readwrite) RCTBridge *bridge;
15 16
 @property (nonatomic, strong, readwrite) RNNStore *store;
17
+@property (nonatomic, strong, readwrite) RNNReactComponentManager *componentManager;
16 18
 
17 19
 @end
18 20
 
@@ -77,7 +79,8 @@
77 79
 	RNNEventEmitter *eventEmitter = [[RNNEventEmitter alloc] init];
78 80
 
79 81
 	id<RNNRootViewCreator> rootViewCreator = [[RNNReactRootViewCreator alloc] initWithBridge:bridge];
80
-	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter andBridge:bridge];
82
+	_componentManager = [[RNNReactComponentManager alloc] initWithCreator:rootViewCreator];
83
+	RNNControllerFactory *controllerFactory = [[RNNControllerFactory alloc] initWithRootViewCreator:rootViewCreator eventEmitter:eventEmitter store:_store componentManager:_componentManager andBridge:bridge];
81 84
 	
82 85
 	_commandsHandler = [[RNNCommandsHandler alloc] initWithStore:_store controllerFactory:controllerFactory eventEmitter:eventEmitter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:[RNNOverlayManager new] mainWindow:_mainWindow];
83 86
 	RNNBridgeModule *bridgeModule = [[RNNBridgeModule alloc] initWithCommandsHandler:_commandsHandler];
@@ -89,6 +92,7 @@
89 92
 
90 93
 - (void)onJavaScriptWillLoad {
91 94
 	[_store clean];
95
+	[_componentManager clean];
92 96
 }
93 97
 
94 98
 - (void)onJavaScriptLoaded {

+ 30
- 24
lib/ios/RNNCommandsHandler.m View File

@@ -58,12 +58,13 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
58 58
 	[_modalManager dismissAllModalsAnimated:NO];
59 59
 	[_store removeAllComponentsFromWindow:_mainWindow];
60 60
 	
61
-	UIViewController *vc = [_controllerFactory createLayout:layout[@"root"] saveToStore:_store];
61
+	UIViewController<RNNLayoutProtocol> *vc = [_controllerFactory createLayout:layout[@"root"]];
62 62
 	
63
-	_mainWindow.rootViewController = vc;
64
-	
65
-	[_eventEmitter sendOnNavigationCommandCompletion:setRoot params:@{@"layout": layout}];
66
-	completion();
63
+	[vc renderTreeAndWait:[vc.resolveOptions.animations.setRoot.waitForRender getWithDefaultValue:NO] perform:^{
64
+		_mainWindow.rootViewController = vc;
65
+		[_eventEmitter sendOnNavigationCommandCompletion:setRoot params:@{@"layout": layout}];
66
+		completion() ;
67
+	}];
67 68
 }
68 69
 
69 70
 - (void)mergeOptions:(NSString*)componentId options:(NSDictionary*)mergeOptions completion:(RNNTransitionCompletionBlock)completion {
@@ -96,7 +97,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
96 97
 - (void)push:(NSString*)componentId layout:(NSDictionary*)layout completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
97 98
 	[self assertReady];
98 99
 	
99
-	UIViewController<RNNLayoutProtocol> *newVc = [_controllerFactory createLayout:layout saveToStore:_store];
100
+	UIViewController<RNNLayoutProtocol> *newVc = [_controllerFactory createLayout:layout];
100 101
 	UIViewController *fromVC = [_store findComponentForId:componentId];
101 102
 	
102 103
 	if ([[newVc.resolveOptions.preview.reactTag getWithDefaultValue:@(0)] floatValue] > 0) {
@@ -141,8 +142,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
141 142
 		}
142 143
 	} else {
143 144
 		id animationDelegate = (newVc.resolveOptions.animations.push.hasCustomAnimation || newVc.getCurrentLeaf.isCustomTransitioned) ? newVc : nil;
144
-		[newVc.getCurrentLeaf waitForReactViewRender:(newVc.resolveOptions.animations.push.waitForRender || animationDelegate) perform:^{
145
-			[_stackManager push:newVc onTop:fromVC animated:newVc.resolveOptions.animations.push.enable animationDelegate:animationDelegate completion:^{
145
+		[newVc renderTreeAndWait:([newVc.resolveOptions.animations.push.waitForRender getWithDefaultValue:NO] || animationDelegate) perform:^{
146
+			[_stackManager push:newVc onTop:fromVC animated:[newVc.resolveOptions.animations.push.enable getWithDefaultValue:YES] animationDelegate:animationDelegate completion:^{
146 147
 				[_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}];
147 148
 				completion();
148 149
 			} rejection:rejection];
@@ -153,11 +154,14 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
153 154
 - (void)setStackRoot:(NSString*)componentId children:(NSArray*)children completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
154 155
 	[self assertReady];
155 156
 	
156
- 	NSArray<RNNLayoutProtocol> *childViewControllers = [_controllerFactory createChildrenLayout:children saveToStore:_store];
157
+ 	NSArray<RNNLayoutProtocol> *childViewControllers = [_controllerFactory createChildrenLayout:children];
158
+	for (UIViewController<RNNLayoutProtocol>* viewController in childViewControllers) {
159
+		[viewController renderTreeAndWait:NO perform:nil];
160
+	}
157 161
 	RNNNavigationOptions* options = [childViewControllers.lastObject getCurrentChild].resolveOptions;
158 162
 	UIViewController *fromVC = [_store findComponentForId:componentId];
159 163
 	__weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter;
160
-	[_stackManager setStackChildren:childViewControllers fromViewController:fromVC animated:options.animations.setStackRoot.enable completion:^{
164
+	[_stackManager setStackChildren:childViewControllers fromViewController:fromVC animated:[options.animations.setStackRoot.enable getWithDefaultValue:YES] completion:^{
161 165
 		[weakEventEmitter sendOnNavigationCommandCompletion:setStackRoot params:@{@"componentId": componentId}];
162 166
 		completion();
163 167
 	} rejection:rejection];
@@ -181,10 +185,10 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
181 185
 	} else {
182 186
 		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;
183 187
 		[vcs removeObject:vc];
184
-		[nvc setViewControllers:vcs animated:vc.resolveOptions.animations.pop.enable];
188
+		[nvc setViewControllers:vcs animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES]];
185 189
 	}
186 190
 	
187
-	[_stackManager pop:vc animated:vc.resolveOptions.animations.pop.enable completion:^{
191
+	[_stackManager pop:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^{
188 192
 		[_store removeComponent:componentId];
189 193
 		[_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}];
190 194
 		completion();
@@ -197,7 +201,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
197 201
 	RNNNavigationOptions *options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
198 202
 	[vc overrideOptions:options];
199 203
 	
200
-	[_stackManager popTo:vc animated:vc.resolveOptions.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
204
+	[_stackManager popTo:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^(NSArray *poppedViewControllers) {
201 205
 		[_eventEmitter sendOnNavigationCommandCompletion:popTo params:@{@"componentId": componentId}];
202 206
 		[self removePopedViewControllers:poppedViewControllers];
203 207
 		completion();
@@ -216,7 +220,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
216 220
 		completion();
217 221
 	}];
218 222
 	
219
-	[_stackManager popToRoot:vc animated:vc.resolveOptions.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
223
+	[_stackManager popToRoot:vc animated:[vc.resolveOptions.animations.pop.enable getWithDefaultValue:YES] completion:^(NSArray *poppedViewControllers) {
220 224
 		[self removePopedViewControllers:poppedViewControllers];
221 225
 	} rejection:^(NSString *code, NSString *message, NSError *error) {
222 226
 		
@@ -228,10 +232,10 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
228 232
 - (void)showModal:(NSDictionary*)layout completion:(RNNTransitionWithComponentIdCompletionBlock)completion {
229 233
 	[self assertReady];
230 234
 	
231
-	UIViewController<RNNParentProtocol> *newVc = [_controllerFactory createLayout:layout saveToStore:_store];
235
+	UIViewController<RNNParentProtocol> *newVc = [_controllerFactory createLayout:layout];
232 236
 	
233
-	[newVc.getCurrentLeaf waitForReactViewRender:newVc.getCurrentLeaf.resolveOptions.animations.showModal.waitForRender perform:^{
234
-		[_modalManager showModal:newVc animated:newVc.getCurrentChild.resolveOptions.animations.showModal.enable hasCustomAnimation:newVc.getCurrentChild.resolveOptions.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) {
237
+	[newVc renderTreeAndWait:[newVc.getCurrentLeaf.resolveOptions.animations.showModal.waitForRender getWithDefaultValue:NO] perform:^{
238
+		[_modalManager showModal:newVc animated:[newVc.getCurrentChild.resolveOptions.animations.showModal.enable getWithDefaultValue:YES] hasCustomAnimation:newVc.getCurrentChild.resolveOptions.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) {
235 239
 			[_eventEmitter sendOnNavigationCommandCompletion:showModal params:@{@"layout": layout}];
236 240
 			completion(componentId);
237 241
 		}];
@@ -272,7 +276,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
272 276
 		completion();
273 277
 	}];
274 278
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:mergeOptions];
275
-	[_modalManager dismissAllModalsAnimated:options.animations.dismissModal.enable];
279
+	[_modalManager dismissAllModalsAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES]];
276 280
 	
277 281
 	[CATransaction commit];
278 282
 }
@@ -280,12 +284,14 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
280 284
 - (void)showOverlay:(NSDictionary *)layout completion:(RNNTransitionCompletionBlock)completion {
281 285
 	[self assertReady];
282 286
 	
283
-	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayout:layout saveToStore:_store];
284
-	UIWindow* overlayWindow = [[RNNOverlayWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
285
-	overlayWindow.rootViewController = overlayVC;
286
-	[_overlayManager showOverlayWindow:overlayWindow];
287
-	[_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}];
288
-	completion();
287
+	UIViewController<RNNParentProtocol>* overlayVC = [_controllerFactory createLayout:layout];
288
+	[overlayVC renderTreeAndWait:[overlayVC.options.animations.showOverlay.waitForRender getWithDefaultValue:NO] perform:^{
289
+		UIWindow* overlayWindow = [[RNNOverlayWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
290
+		overlayWindow.rootViewController = overlayVC;
291
+		[_overlayManager showOverlayWindow:overlayWindow];
292
+		[_eventEmitter sendOnNavigationCommandCompletion:showOverlay params:@{@"layout": layout}];
293
+		completion();
294
+	}];
289 295
 }
290 296
 
291 297
 - (void)dismissOverlay:(NSString*)componentId completion:(RNNTransitionCompletionBlock)completion rejection:(RNNTransitionRejectionBlock)reject {

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

@@ -5,5 +5,6 @@
5 5
 @property (nonatomic, strong) Text* name;
6 6
 @property (nonatomic, strong) Text* componentId;
7 7
 @property (nonatomic, strong) Text* alignment;
8
+@property (nonatomic, strong) Bool* waitForRender;
8 9
 
9 10
 @end

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

@@ -8,6 +8,7 @@
8 8
 	self.name = [TextParser parse:dict key:@"name"];
9 9
 	self.componentId = [TextParser parse:dict key:@"componentId"];
10 10
 	self.alignment = [TextParser parse:dict key:@"alignment"];
11
+	self.waitForRender = [BoolParser parse:dict key:@"waitForRender"];
11 12
 	
12 13
 	return self;
13 14
 }

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

@@ -5,16 +5,19 @@
5 5
 #import "RNNStore.h"
6 6
 #import "RNNEventEmitter.h"
7 7
 #import "RNNParentProtocol.h"
8
+#import "RNNReactComponentManager.h"
8 9
 
9 10
 @interface RNNControllerFactory : NSObject
10 11
 
11 12
 -(instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
12 13
 						  eventEmitter:(RNNEventEmitter*)eventEmitter
14
+								 store:(RNNStore *)store
15
+					  componentManager:(RNNReactComponentManager *)componentManager
13 16
 							 andBridge:(RCTBridge*)bridge;
14 17
 
15
-- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store;
18
+- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout;
16 19
 
17
-- (NSArray<RNNLayoutProtocol> *)createChildrenLayout:(NSArray*)children saveToStore:(RNNStore *)store;
20
+- (NSArray<RNNLayoutProtocol> *)createChildrenLayout:(NSArray*)children;
18 21
 
19 22
 @property (nonatomic, strong) RNNEventEmitter *eventEmitter;
20 23
 

+ 10
- 17
lib/ios/RNNControllerFactory.m View File

@@ -20,6 +20,7 @@
20 20
 	id<RNNRootViewCreator> _creator;
21 21
 	RNNStore *_store;
22 22
 	RCTBridge *_bridge;
23
+	RNNReactComponentManager* _componentManager;
23 24
 }
24 25
 
25 26
 # pragma mark public
@@ -27,6 +28,8 @@
27 28
 
28 29
 - (instancetype)initWithRootViewCreator:(id <RNNRootViewCreator>)creator
29 30
 						   eventEmitter:(RNNEventEmitter*)eventEmitter
31
+								  store:(RNNStore *)store
32
+					   componentManager:(RNNReactComponentManager *)componentManager
30 33
 							  andBridge:(RCTBridge *)bridge {
31 34
 	
32 35
 	self = [super init];
@@ -34,24 +37,22 @@
34 37
 	_creator = creator;
35 38
 	_eventEmitter = eventEmitter;
36 39
 	_bridge = bridge;
40
+	_store = store;
41
+	_componentManager = componentManager;
37 42
 	
38 43
 	return self;
39 44
 }
40 45
 
41
-- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout saveToStore:(RNNStore *)store {
42
-	_store = store;
46
+- (UIViewController<RNNParentProtocol> *)createLayout:(NSDictionary*)layout {
43 47
 	UIViewController<RNNParentProtocol>* layoutViewController = [self fromTree:layout];
44
-	_store = nil;
45 48
 	return layoutViewController;
46 49
 }
47 50
 
48
-- (NSArray<RNNLayoutProtocol> *)createChildrenLayout:(NSArray*)children saveToStore:(RNNStore *)store {
49
-	_store = store;
51
+- (NSArray<RNNLayoutProtocol> *)createChildrenLayout:(NSArray*)children {
50 52
 	NSMutableArray<RNNLayoutProtocol>* childViewControllers = [NSMutableArray<RNNLayoutProtocol> new];
51 53
 	for (NSDictionary* layout in children) {
52 54
 		[childViewControllers addObject:[self fromTree:layout]];
53 55
 	}
54
-	_store = nil;
55 56
 	return childViewControllers;
56 57
 }
57 58
 
@@ -114,17 +115,10 @@
114 115
 - (UIViewController<RNNParentProtocol> *)createComponent:(RNNLayoutNode*)node {
115 116
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
116 117
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];;
117
-	
118
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
119
-
118
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithComponentManager:_componentManager];
120 119
 	
121 120
 	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter presenter:presenter options:options defaultOptions:_defaultOptions];
122 121
 	
123
-	if (!component.isExternalViewController) {
124
-		CGSize availableSize = UIApplication.sharedApplication.delegate.window.bounds.size;
125
-		[_bridge.uiManager setAvailableSize:availableSize forRootView:component.view];
126
-	}
127
-	
128 122
 	return (UIViewController<RNNParentProtocol> *)component;
129 123
 }
130 124
 
@@ -143,14 +137,13 @@
143 137
 
144 138
 
145 139
 - (UIViewController<RNNParentProtocol> *)createStack:(RNNLayoutNode*)node {
146
-	RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] init];
147
-	
140
+	RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] initWithComponentManager:_componentManager];
148 141
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
149 142
 	RNNNavigationOptions* options = [[RNNNavigationOptions alloc] initWithDict:node.data[@"options"]];;
150 143
 	
151 144
 	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
152 145
 	
153
-	RNNNavigationController* stack = [[RNNNavigationController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options defaultOptions:_defaultOptions presenter:presenter];
146
+	RNNNavigationController* stack = [[RNNNavigationController alloc] initWithLayoutInfo:layoutInfo creator:_creator childViewControllers:childViewControllers options:options defaultOptions:_defaultOptions presenter:presenter];
154 147
 	
155 148
 	return stack;
156 149
 }

+ 5
- 1
lib/ios/RNNLayoutProtocol.h View File

@@ -1,6 +1,8 @@
1 1
 #import "RNNLayoutInfo.h"
2
-#import "RNNViewControllerPresenter.h"
3 2
 #import "RNNLeafProtocol.h"
3
+#import "RNNBasePresenter.h"
4
+
5
+typedef void (^RNNReactViewReadyCompletionBlock)(void);
4 6
 
5 7
 @protocol RNNLayoutProtocol <NSObject, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate, UISplitViewControllerDelegate>
6 8
 
@@ -11,6 +13,8 @@
11 13
 @property (nonatomic, strong) RNNNavigationOptions* options;
12 14
 @property (nonatomic, strong) RNNNavigationOptions* defaultOptions;
13 15
 
16
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock;
17
+
14 18
 - (UIViewController<RNNLayoutProtocol> *)getCurrentChild;
15 19
 
16 20
 - (UIViewController<RNNLeafProtocol, RNNLayoutProtocol> *)getCurrentLeaf;

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

@@ -1,15 +1,8 @@
1
-#import "RNNRootViewCreator.h"
2
-
3
-typedef void (^RNNReactViewReadyCompletionBlock)(void);
4 1
 
5 2
 @protocol RNNLeafProtocol <NSObject>
6 3
 
7
-- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock;
8
-
9 4
 - (void)bindViewController:(UIViewController *)viewController;
10 5
 
11 6
 - (BOOL)isCustomTransitioned;
12 7
 
13
-- (id<RNNRootViewCreator>)creator;
14
-
15 8
 @end

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

@@ -73,7 +73,7 @@
73 73
 	}
74 74
 
75 75
 	if (modalToDismiss == topPresentedVC || [[topPresentedVC childViewControllers] containsObject:modalToDismiss]) {
76
-		[modalToDismiss dismissViewControllerAnimated:options.animations.dismissModal.enable completion:^{
76
+		[modalToDismiss dismissViewControllerAnimated:[options.animations.dismissModal.enable getWithDefaultValue:YES] completion:^{
77 77
 			[_pendingModalIdsToDismiss removeObject:modalToDismiss];
78 78
 			if (modalToDismiss.view) {
79 79
 				[self dismissedModal:modalToDismiss];
@@ -88,7 +88,7 @@
88 88
 	} else {
89 89
 		[modalToDismiss.view removeFromSuperview];
90 90
 		modalToDismiss.view = nil;
91
-		modalToDismiss.getCurrentChild.resolveOptions.animations.dismissModal.enable = NO;
91
+		modalToDismiss.getCurrentChild.resolveOptions.animations.dismissModal.enable = [[Bool alloc] initWithBOOL:NO];
92 92
 		[self dismissedModal:modalToDismiss];
93 93
 		
94 94
 		if (completion) {

+ 2
- 1
lib/ios/RNNNavigationButtons.h View File

@@ -2,10 +2,11 @@
2 2
 #import <UIKit/UIKit.h>
3 3
 #import "RNNButtonOptions.h"
4 4
 #import "RNNRootViewCreator.h"
5
+#import "RNNReactComponentManager.h"
5 6
 
6 7
 @interface RNNNavigationButtons : NSObject
7 8
 
8
--(instancetype)initWithViewController:(UIViewController*)viewController rootViewCreator:(id<RNNRootViewCreator>)creator;
9
+-(instancetype)initWithViewController:(UIViewController*)viewController componentManager:(RNNReactComponentManager *)componentManager;
9 10
 
10 11
 -(void)applyLeftButtons:(NSArray*)leftButtons rightButtons:(NSArray*)rightButtons defaultLeftButtonStyle:(RNNButtonOptions *)defaultLeftButtonStyle defaultRightButtonStyle:(RNNButtonOptions *)defaultRightButtonStyle;
11 12
 

+ 9
- 5
lib/ios/RNNNavigationButtons.m View File

@@ -7,19 +7,19 @@
7 7
 
8 8
 @interface RNNNavigationButtons()
9 9
 
10
-@property (weak, nonatomic) UIViewController* viewController;
10
+@property (weak, nonatomic) UIViewController<RNNLayoutProtocol>* viewController;
11 11
 @property (strong, nonatomic) RNNButtonOptions* defaultLeftButtonStyle;
12 12
 @property (strong, nonatomic) RNNButtonOptions* defaultRightButtonStyle;
13
-@property (nonatomic) id<RNNRootViewCreator> creator;
13
+@property (strong, nonatomic) RNNReactComponentManager* componentManager;
14 14
 @end
15 15
 
16 16
 @implementation RNNNavigationButtons
17 17
 
18
--(instancetype)initWithViewController:(UIViewController*)viewController rootViewCreator:(id<RNNRootViewCreator>)creator {
18
+-(instancetype)initWithViewController:(UIViewController<RNNLayoutProtocol>*)viewController componentManager:(id)componentManager {
19 19
 	self = [super init];
20 20
 	
21 21
 	self.viewController = viewController;
22
-	self.creator = creator;
22
+	self.componentManager = componentManager;
23 23
 	
24 24
 	return self;
25 25
 }
@@ -85,7 +85,11 @@
85 85
 	
86 86
 	RNNUIBarButtonItem *barButtonItem;
87 87
 	if (component) {
88
-		RCTRootView *view = (RCTRootView*)[self.creator createCustomReactView:component[@"name"] rootViewId:component[@"componentId"]];
88
+		RNNComponentOptions* componentOptions = [RNNComponentOptions new];
89
+		componentOptions.componentId = [[Text alloc] initWithValue:component[@"componentId"]];
90
+		componentOptions.name = [[Text alloc] initWithValue:component[@"name"]];
91
+		
92
+		RNNReactView *view = [_componentManager createComponentIfNotExists:componentOptions parentComponentId:self.viewController.layoutInfo.componentId reactViewReadyBlock:nil];
89 93
 		barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withCustomView:view];
90 94
 	} else if (iconImage) {
91 95
 		barButtonItem = [[RNNUIBarButtonItem alloc] init:buttonId withIcon:iconImage];

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

@@ -10,6 +10,9 @@
10 10
 @property (nonatomic, strong) RNNNavigationOptions* options;
11 11
 @property (nonatomic, strong) RNNNavigationOptions* defaultOptions;
12 12
 
13
+
14
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo creator:(id<RNNRootViewCreator>)creator childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNBasePresenter *)presenter;
15
+
13 16
 - (void)setTopBarBackgroundColor:(UIColor *)backgroundColor;
14 17
 
15 18
 @end

+ 26
- 1
lib/ios/RNNNavigationController.m View File

@@ -13,7 +13,7 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
13 13
 
14 14
 @implementation RNNNavigationController
15 15
 
16
-- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNNavigationControllerPresenter *)presenter {
16
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo creator:(id<RNNRootViewCreator>)creator childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNNavigationControllerPresenter *)presenter {
17 17
 	self = [super init];
18 18
 
19 19
 	self.presenter = presenter;
@@ -37,6 +37,7 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
37 37
 
38 38
 - (void)onChildWillAppear {
39 39
 	[_presenter applyOptions:self.resolveOptions];
40
+	[_presenter renderComponents:self.resolveOptions perform:nil];
40 41
 	[((UIViewController<RNNParentProtocol> *)self.parentViewController) onChildWillAppear];
41 42
 }
42 43
 
@@ -53,6 +54,30 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
53 54
 	[self.options overrideOptions:options];
54 55
 }
55 56
 
57
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
58
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
59
+		dispatch_group_t group = dispatch_group_create();
60
+		for (UIViewController<RNNLayoutProtocol>* childViewController in self.childViewControllers) {
61
+			dispatch_group_enter(group);
62
+			dispatch_async(dispatch_get_main_queue(), ^{
63
+				[childViewController renderTreeAndWait:wait perform:^{
64
+					dispatch_group_leave(group);
65
+				}];
66
+			});
67
+		}
68
+		
69
+		dispatch_group_enter(group);
70
+		[self.presenter renderComponents:self.resolveOptions perform:^{
71
+			dispatch_group_leave(group);
72
+		}];
73
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
74
+		
75
+		dispatch_async(dispatch_get_main_queue(), ^{
76
+			readyBlock();
77
+		});
78
+	});
79
+}
80
+
56 81
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
57 82
 	return self.getCurrentChild.supportedInterfaceOrientations;
58 83
 }

+ 6
- 0
lib/ios/RNNNavigationControllerPresenter.h View File

@@ -1,7 +1,13 @@
1 1
 #import "RNNBasePresenter.h"
2
+#import "RNNRootViewCreator.h"
3
+#import "RNNReactComponentManager.h"
2 4
 
3 5
 @interface RNNNavigationControllerPresenter : RNNBasePresenter
4 6
 
7
+- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager;
8
+
5 9
 - (void)applyOptionsBeforePopping:(RNNNavigationOptions *)options;
6 10
 
11
+- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
12
+
7 13
 @end

+ 98
- 0
lib/ios/RNNNavigationControllerPresenter.m View File

@@ -2,9 +2,27 @@
2 2
 #import "UINavigationController+RNNOptions.h"
3 3
 #import "RNNNavigationController.h"
4 4
 #import <React/RCTConvert.h>
5
+#import "RNNCustomTitleView.h"
5 6
 
7
+@interface RNNNavigationControllerPresenter() {
8
+	RNNReactComponentManager* _componentManager;
9
+	UIView* _customTopBar;
10
+	UIView* _customTopBarBackground;
11
+}
12
+
13
+@end
6 14
 @implementation RNNNavigationControllerPresenter
7 15
 
16
+- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager {
17
+	self = [super init];
18
+	_componentManager = componentManager;
19
+	return self;
20
+}
21
+
22
+- (void)bindViewController:(UIViewController *)bindedViewController {
23
+	self.bindedViewController = bindedViewController;
24
+}
25
+
8 26
 - (void)applyOptions:(RNNNavigationOptions *)options {
9 27
 	[super applyOptions:options];
10 28
 	
@@ -112,6 +130,86 @@
112 130
 	
113 131
 	[navigationController rnn_setNavigationBarFontFamily:[newOptions.topBar.title.fontFamily getWithDefaultValue:nil] fontSize:[newOptions.topBar.title.fontSize getWithDefaultValue:nil] color:[newOptions.topBar.title.color getWithDefaultValue:nil]];
114 132
 	
133
+	if (newOptions.topBar.component.name.hasValue) {
134
+		[self setCustomNavigationBarView:newOptions perform:nil];
135
+	}
136
+	
137
+	if (newOptions.topBar.background.component.name.hasValue) {
138
+		[self setCustomNavigationComponentBackground:newOptions perform:nil];
139
+	}
140
+}
141
+
142
+- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
143
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
144
+		dispatch_group_t group = dispatch_group_create();
145
+		
146
+		dispatch_group_enter(group);
147
+		dispatch_async(dispatch_get_main_queue(), ^{
148
+			[self setCustomNavigationBarView:options perform:^{
149
+				dispatch_group_leave(group);
150
+			}];
151
+		});
152
+		
153
+		dispatch_group_enter(group);
154
+		dispatch_async(dispatch_get_main_queue(), ^{
155
+			[self setCustomNavigationComponentBackground:options perform:^{
156
+				dispatch_group_leave(group);
157
+			}];
158
+		});
159
+		
160
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
161
+		
162
+		dispatch_async(dispatch_get_main_queue(), ^{
163
+			if (readyBlock) {
164
+				readyBlock();
165
+			}
166
+		});
167
+	});
168
+}
169
+
170
+- (void)setCustomNavigationBarView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
171
+	RNNNavigationController* navigationController = self.bindedViewController;
172
+	if (![options.topBar.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
173
+		readyBlock();
174
+		readyBlock = nil;
175
+	}
176
+	if (options.topBar.component.name.hasValue) {
177
+		RCTRootView *reactView = [_componentManager createComponentIfNotExists:options.topBar.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
178
+		
179
+		_customTopBar = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
180
+		reactView.backgroundColor = UIColor.clearColor;
181
+		_customTopBar.backgroundColor = UIColor.clearColor;
182
+		[navigationController.navigationBar addSubview:_customTopBar];
183
+	} else {
184
+		[_customTopBar removeFromSuperview];
185
+		if (readyBlock) {
186
+			readyBlock();
187
+		}
188
+	}
189
+}
190
+
191
+- (void)setCustomNavigationComponentBackground:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
192
+	RNNNavigationController* navigationController = self.bindedViewController;
193
+	if (![options.topBar.background.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
194
+		readyBlock();
195
+		readyBlock = nil;
196
+	}
197
+	if (options.topBar.background.component.name.hasValue) {
198
+		RCTRootView *reactView = [_componentManager createComponentIfNotExists:options.topBar.background.component parentComponentId:navigationController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
199
+		
200
+		_customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
201
+		[navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1];
202
+	} else {
203
+		[_customTopBarBackground removeFromSuperview];
204
+		if (readyBlock) {
205
+			readyBlock();
206
+		}
207
+	}
208
+}
209
+
210
+- (void)dealloc {
211
+	RNNNavigationController* navigationController = self.bindedViewController;
212
+	[_componentManager removeComponent:navigationController.layoutInfo.componentId];
115 213
 }
116 214
 
117 215
 @end

+ 2
- 1
lib/ios/RNNParentProtocol.h View File

@@ -3,10 +3,11 @@
3 3
 
4 4
 @protocol RNNParentProtocol <RNNLayoutProtocol, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate, UISplitViewControllerDelegate>
5 5
 
6
-@required
7 6
 
7
+@optional
8 8
 - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options defaultOptions:(RNNNavigationOptions *)defaultOptions presenter:(RNNBasePresenter *)presenter;
9 9
 
10
+@required
10 11
 - (void)onChildWillAppear;
11 12
 
12 13
 @end

+ 17
- 0
lib/ios/RNNReactComponentManager.h View File

@@ -0,0 +1,17 @@
1
+#import <Foundation/Foundation.h>
2
+#import "RNNReactView.h"
3
+#import "RNNComponentOptions.h"
4
+#import "RNNStore.h"
5
+#import "RNNRootViewCreator.h"
6
+
7
+@interface RNNReactComponentManager : NSObject
8
+
9
+- (instancetype)initWithCreator:(id<RNNRootViewCreator>)creator;
10
+
11
+- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
12
+
13
+- (void)removeComponent:(NSString *)componentId;
14
+
15
+- (void)clean;
16
+
17
+@end

+ 52
- 0
lib/ios/RNNReactComponentManager.m View File

@@ -0,0 +1,52 @@
1
+#import "RNNReactComponentManager.h"
2
+
3
+@interface RNNReactComponentManager () {
4
+	id<RNNRootViewCreator> _creator;
5
+	NSMutableDictionary* _componentStore;
6
+}
7
+
8
+@end
9
+
10
+@implementation RNNReactComponentManager
11
+
12
+- (instancetype)initWithCreator:(id<RNNRootViewCreator>)creator {
13
+	self = [super init];
14
+	_creator = creator;
15
+	_componentStore = [NSMutableDictionary new];
16
+	return self;
17
+}
18
+
19
+- (RNNReactView *)createComponentIfNotExists:(RNNComponentOptions *)component parentComponentId:(NSString *)parentComponentId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
20
+	NSMutableDictionary* 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;
31
+}
32
+
33
+- (NSMutableDictionary *)componentsForParentId:(NSString *)parentComponentId {
34
+	if (!_componentStore[parentComponentId]) {
35
+		[_componentStore setObject:[NSMutableDictionary new] forKey:parentComponentId];;
36
+	}
37
+	
38
+	return [_componentStore objectForKey:parentComponentId];;
39
+}
40
+
41
+- (void)removeComponent:(NSString *)componentId {
42
+	if ([_componentStore objectForKey:componentId]) {
43
+		[_componentStore removeObjectForKey:componentId];
44
+	}
45
+}
46
+
47
+- (void)clean {
48
+	[_componentStore removeAllObjects];
49
+}
50
+
51
+
52
+@end

+ 0
- 6
lib/ios/RNNReactRootView.h View File

@@ -1,6 +0,0 @@
1
-#import <UIKit/UIKit.h>
2
-#import "RNNReactView.h"
3
-
4
-@interface RNNReactRootView : RNNReactView
5
-
6
-@end

+ 0
- 16
lib/ios/RNNReactRootView.m View File

@@ -1,16 +0,0 @@
1
-#import "RNNReactRootView.h"
2
-#import "RCTHelpers.h"
3
-#import <React/RCTUIManager.h>
4
-
5
-@implementation RNNReactRootView
6
-
7
-- (void)layoutSubviews {
8
-	[super layoutSubviews];
9
-	[self.bridge.uiManager setSize:self.bounds.size forView:self];
10
-}
11
-
12
-- (void)contentDidAppear:(NSNotification *)notification {
13
-	
14
-}
15
-
16
-@end

+ 10
- 18
lib/ios/RNNReactRootViewCreator.m View File

@@ -1,45 +1,37 @@
1 1
 
2 2
 #import "RNNReactRootViewCreator.h"
3
-#import "RNNReactRootView.h"
4 3
 #import "RNNReactView.h"
5 4
 
6 5
 @implementation RNNReactRootViewCreator {
7 6
 	RCTBridge *_bridge;
8 7
 }
9 8
 
10
--(instancetype)initWithBridge:(RCTBridge*)bridge {
9
+- (instancetype)initWithBridge:(RCTBridge*)bridge {
11 10
 	self = [super init];
12 11
 	
13 12
 	_bridge = bridge;
14 13
 	
15 14
 	return self;
16
-	
17 15
 }
18 16
 
19
-- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId {
17
+- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
20 18
 	if (!rootViewId) {
21 19
 		@throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil];
22 20
 	}
23 21
 	
24
-	UIView *view = [[RNNReactRootView alloc] initWithBridge:_bridge
25
-										 moduleName:name
26
-								  initialProperties:@{@"componentId": rootViewId}];
22
+	RNNReactView *view = [[RNNReactView alloc] initWithBridge:_bridge
23
+														   moduleName:name
24
+													initialProperties:@{@"componentId": rootViewId}
25
+												  reactViewReadyBlock:reactViewReadyBlock];
27 26
 	return view;
28 27
 }
29 28
 
30
-- (UIView*)createCustomReactView:(NSString*)name rootViewId:(NSString*)rootViewId {
31
-	if (!rootViewId) {
32
-		@throw [NSException exceptionWithName:@"MissingViewId" reason:@"Missing view id" userInfo:nil];
33
-	}
34
-	
35
-	UIView *view = [[RNNReactView alloc] initWithBridge:_bridge
36
-												 moduleName:name
37
-										  initialProperties:@{@"componentId": rootViewId}];
38
-	return view;
29
+- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions {
30
+	return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:nil];
39 31
 }
40 32
 
41
--(UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions {
42
-	return [self createCustomReactView:componentOptions.name.get rootViewId:componentOptions.componentId.get];
33
+- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
34
+	return [self createRootView:componentOptions.name.get rootViewId:componentOptions.componentId.get reactViewReadyBlock:reactViewReadyBlock];
43 35
 }
44 36
 
45 37
 @end

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

@@ -1,9 +1,14 @@
1 1
 #import <React/RCTRootView.h>
2 2
 #import <React/RCTRootViewDelegate.h>
3 3
 
4
+typedef void (^RNNReactViewReadyCompletionBlock)(void);
5
+
4 6
 @interface RNNReactView : RCTRootView <RCTRootViewDelegate>
5 7
 
8
+- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
9
+
6 10
 @property (nonatomic, copy) void (^rootViewDidChangeIntrinsicSize)(CGSize intrinsicSize);
11
+@property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock;
7 12
 
8 13
 - (void)setAlignment:(NSString *)alignment;
9 14
 

+ 13
- 5
lib/ios/RNNReactView.m View File

@@ -1,23 +1,31 @@
1 1
 #import "RNNReactView.h"
2 2
 #import "RCTHelpers.h"
3
+#import <React/RCTUIManager.h>
3 4
 
4 5
 @implementation RNNReactView
5 6
 
6
-- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties {
7
+- (instancetype)initWithBridge:(RCTBridge *)bridge moduleName:(NSString *)moduleName initialProperties:(NSDictionary *)initialProperties reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock {
7 8
 	self = [super initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
8
-	
9
-#ifdef DEBUG
10 9
 	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentDidAppear:) name:RCTContentDidAppearNotification object:nil];
11
-#endif
10
+	 _reactViewReadyBlock = reactViewReadyBlock;
12 11
 	
13 12
 	return self;
14 13
 }
15 14
 
16 15
 - (void)contentDidAppear:(NSNotification *)notification {
16
+#ifdef DEBUG
17 17
 	if ([((RNNReactView *)notification.object).moduleName isEqualToString:self.moduleName]) {
18 18
 		[RCTHelpers removeYellowBox:self];
19
-		[[NSNotificationCenter defaultCenter] removeObserver:self];
20 19
 	}
20
+#endif
21
+	
22
+	RNNReactView* appearedView = notification.object;
23
+	
24
+	 if (_reactViewReadyBlock && [appearedView.appProperties[@"componentId"] isEqual:self.appProperties[@"componentId"]]) {
25
+	 	_reactViewReadyBlock();
26
+		 _reactViewReadyBlock = nil;
27
+		 [[NSNotificationCenter defaultCenter] removeObserver:self];
28
+	 }
21 29
 }
22 30
 
23 31
 - (void)setRootViewDidChangeIntrinsicSize:(void (^)(CGSize))rootViewDidChangeIntrinsicSize {

+ 24
- 135
lib/ios/RNNRootViewController.m View File

@@ -1,22 +1,10 @@
1 1
 #import "RNNRootViewController.h"
2 2
 #import <React/RCTConvert.h>
3 3
 #import "RNNAnimator.h"
4
-#import "RNNCustomTitleView.h"
5 4
 #import "RNNPushAnimation.h"
6 5
 #import "RNNReactView.h"
7 6
 #import "RNNParentProtocol.h"
8
-#import "RNNTitleViewHelper.h"
9 7
 
10
-@interface RNNRootViewController() {
11
-	RNNReactView* _customTitleView;
12
-	UIView* _customTopBar;
13
-	UIView* _customTopBarBackground;
14
-	BOOL _isBeingPresented;
15
-}
16
-
17
-@property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock;
18
-
19
-@end
20 8
 
21 9
 @implementation RNNRootViewController
22 10
 
@@ -27,17 +15,10 @@
27 15
 	
28 16
 	self.layoutInfo = layoutInfo;
29 17
 	self.creator = creator;
30
-	if (self.creator) {
31
-		self.view = [creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId];
32
-		[[NSNotificationCenter defaultCenter] addObserver:self
33
-												 selector:@selector(reactViewReady)
34
-													 name: @"RCTContentDidAppearNotification"
35
-												   object:nil];
36
-	}
37 18
 	
38 19
 	self.eventEmitter = eventEmitter;
39 20
 	self.presenter = presenter;
40
-	[self.presenter bindViewController:self viewCreator:self.creator];
21
+	[self.presenter bindViewController:self];
41 22
 	self.options = options;
42 23
 	self.defaultOptions = defaultOptions;
43 24
 	
@@ -74,8 +55,6 @@
74 55
 - (void)mergeOptions:(RNNNavigationOptions *)options {
75 56
 	[_presenter mergeOptions:options currentOptions:self.options defaultOptions:self.defaultOptions];
76 57
 	[((UIViewController<RNNLayoutProtocol> *)self.parentViewController) mergeOptions:options];
77
-	
78
-	[self initCustomViews];
79 58
 }
80 59
 
81 60
 - (void)overrideOptions:(RNNNavigationOptions *)options {
@@ -84,12 +63,11 @@
84 63
 
85 64
 - (void)viewWillAppear:(BOOL)animated{
86 65
 	[super viewWillAppear:animated];
87
-	_isBeingPresented = YES;
88 66
 	
89 67
 	[_presenter applyOptions:self.resolveOptions];
90
-	[((UIViewController<RNNParentProtocol> *)self.parentViewController) onChildWillAppear];
68
+	[_presenter renderComponents:self.resolveOptions perform:nil];
91 69
 	
92
-	[self initCustomViews];
70
+	[((UIViewController<RNNParentProtocol> *)self.parentViewController) onChildWillAppear];
93 71
 }
94 72
 
95 73
 - (RNNNavigationOptions *)resolveOptions {
@@ -103,7 +81,6 @@
103 81
 
104 82
 - (void)viewWillDisappear:(BOOL)animated {
105 83
 	[super viewWillDisappear:animated];
106
-	_isBeingPresented = NO;
107 84
 }
108 85
 
109 86
 - (void)viewDidDisappear:(BOOL)animated {
@@ -111,21 +88,29 @@
111 88
 	[self.eventEmitter sendComponentDidDisappear:self.layoutInfo.componentId componentName:self.layoutInfo.name];
112 89
 }
113 90
 
114
-- (void)reactViewReady {
115
-	if (_reactViewReadyBlock) {
116
-		_reactViewReadyBlock();
117
-		_reactViewReadyBlock = nil;
91
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
92
+	if (self.isExternalViewController) {
93
+		if (readyBlock) {
94
+			readyBlock();
95
+		}
96
+		return;
118 97
 	}
119 98
 	
120
-	[[NSNotificationCenter defaultCenter] removeObserver:self name:@"RCTContentDidAppearNotification" object:nil];
121
-}
122
-
123
-
124
-- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
125
-	if (wait && !self.isExternalViewController) {
126
-		[self onReactViewReady:readyBlock];
127
-	} else {
128
-		readyBlock();
99
+	__block RNNReactViewReadyCompletionBlock readyBlockCopy = readyBlock;
100
+	UIView* reactView = [_creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId reactViewReadyBlock:^{
101
+		[_presenter renderComponents:self.resolveOptions perform:^{
102
+			if (readyBlockCopy) {
103
+				readyBlockCopy();
104
+				readyBlockCopy = nil;
105
+			}
106
+		}];
107
+	}];
108
+	reactView.backgroundColor = [UIColor clearColor];
109
+	self.view = reactView;
110
+	
111
+	if (!wait && readyBlock) {
112
+		readyBlockCopy();
113
+		readyBlockCopy = nil;
129 114
 	}
130 115
 }
131 116
 
@@ -137,14 +122,6 @@
137 122
 	return self;
138 123
 }
139 124
 
140
-- (void)onReactViewReady:(RNNReactViewReadyCompletionBlock)readyBlock {
141
-	if (self.isExternalViewController) {
142
-		readyBlock();
143
-	} else {
144
-		self.reactViewReadyBlock = readyBlock;
145
-	}
146
-}
147
-
148 125
 -(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
149 126
 	[self.eventEmitter sendOnSearchBarUpdated:self.layoutInfo.componentId
150 127
 										 text:searchController.searchBar.text
@@ -155,91 +132,6 @@
155 132
 	[self.eventEmitter sendOnSearchBarCancelPressed:self.layoutInfo.componentId];
156 133
 }
157 134
 
158
-- (void)initCustomViews {
159
-	[self setCustomNavigationTitleView];
160
-	[self setCustomNavigationBarView];
161
-	[self setCustomNavigationComponentBackground];
162
-	
163
-	if (!_customTitleView) {
164
-		[self setTitleViewWithSubtitle];
165
-	}
166
-}
167
-
168
-- (void)setTitleViewWithSubtitle {
169
-	if (self.resolveOptions.topBar.subtitle.text.hasValue) {
170
-		RNNTitleViewHelper* titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:self.resolveOptions.topBar.title subTitleOptions:self.resolveOptions.topBar.subtitle viewController:self];
171
-		[titleViewHelper setup];
172
-	}
173
-}
174
-
175
-- (void)setCustomNavigationTitleView {
176
-	if (!_customTitleView && _isBeingPresented) {
177
-		if (self.resolveOptions.topBar.title.component.name.hasValue) {
178
-			_customTitleView = (RNNReactView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.title.component];
179
-			_customTitleView.backgroundColor = UIColor.clearColor;
180
-			NSString* alignment = [self.resolveOptions.topBar.title.component.alignment getWithDefaultValue:@""];
181
-			[_customTitleView setAlignment:alignment];
182
-			BOOL isCenter = [alignment isEqualToString:@"center"];
183
-			__weak RNNReactView *weakTitleView = _customTitleView;
184
-			CGRect frame = self.navigationController.navigationBar.bounds;
185
-			[_customTitleView setFrame:frame];
186
-			[_customTitleView setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicContentSize) {
187
-				if (isCenter) {
188
-					[weakTitleView setFrame:CGRectMake(0, 0, intrinsicContentSize.width, intrinsicContentSize.height)];
189
-				} else {
190
-					[weakTitleView setFrame:frame];
191
-				}
192
-			}];
193
-			
194
-			self.navigationItem.titleView = _customTitleView;
195
-		}
196
-	} else if (_customTitleView && _customTitleView.superview == nil) {
197
-		if ([self.navigationItem.title isKindOfClass:[RNNCustomTitleView class]] && !_customTitleView) {
198
-			self.navigationItem.title = nil;
199
-		}
200
-		self.navigationItem.titleView = _customTitleView;
201
-	}
202
-}
203
-
204
-- (void)setCustomNavigationBarView {
205
-	if (!_customTopBar) {
206
-		if (self.resolveOptions.topBar.component.name.hasValue) {
207
-			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.component];
208
-			
209
-			_customTopBar = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
210
-			reactView.backgroundColor = UIColor.clearColor;
211
-			_customTopBar.backgroundColor = UIColor.clearColor;
212
-			[self.navigationController.navigationBar addSubview:_customTopBar];
213
-		} else if ([[self.navigationController.navigationBar.subviews lastObject] isKindOfClass:[RNNCustomTitleView class]] && !_customTopBar) {
214
-			[[self.navigationController.navigationBar.subviews lastObject] removeFromSuperview];
215
-		}
216
-	} else if (_customTopBar && _customTopBar.superview == nil) {
217
-		if ([[self.navigationController.navigationBar.subviews lastObject] isKindOfClass:[RNNCustomTitleView class]] && !_customTopBar) {
218
-			[[self.navigationController.navigationBar.subviews lastObject] removeFromSuperview];
219
-		}
220
-		[self.navigationController.navigationBar addSubview:_customTopBar];
221
-	}
222
-}
223
-
224
-- (void)setCustomNavigationComponentBackground {
225
-	if (!_customTopBarBackground) {
226
-		if (self.resolveOptions.topBar.background.component.name.hasValue) {
227
-			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.resolveOptions.topBar.background.component];
228
-			
229
-			_customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
230
-			[self.navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1];
231
-		} else if (self.navigationController.navigationBar.subviews.count > 1 && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) {
232
-			[[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview];
233
-		}
234
-	} if (_customTopBarBackground && _customTopBarBackground.superview == nil) {
235
-		if (self.navigationController.navigationBar.subviews.count && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) {
236
-			[[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview];
237
-		}
238
-		[self.navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1];
239
-		self.navigationController.navigationBar.clipsToBounds = YES;
240
-	}
241
-}
242
-
243 135
 -(BOOL)isCustomTransitioned {
244 136
 	return self.resolveOptions.customTransition.animations != nil;
245 137
 }
@@ -376,9 +268,6 @@
376 268
 	self.navigationItem.titleView = nil;
377 269
 	self.navigationItem.rightBarButtonItems = nil;
378 270
 	self.navigationItem.leftBarButtonItems = nil;
379
-	_customTopBar = nil;
380
-	_customTitleView = nil;
381
-	_customTopBarBackground = nil;
382 271
 }
383 272
 
384 273
 @end

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

@@ -1,14 +1,15 @@
1 1
 
2 2
 #import <UIKit/UIKit.h>
3 3
 #import "RNNComponentOptions.h"
4
+#import "RNNReactView.h"
4 5
 
5 6
 @protocol RNNRootViewCreator
6 7
 
7
-- (UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId;
8
+- (RNNReactView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
8 9
 
9 10
 - (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions;
10 11
 
11
-- (UIView*)createCustomReactView:(NSString*)name rootViewId:(NSString*)rootViewId;
12
+- (UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions reactViewReadyBlock:(RNNReactViewReadyCompletionBlock)reactViewReadyBlock;
12 13
 
13 14
 @end
14 15
 

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

@@ -7,8 +7,8 @@
7 7
 @property (nonatomic, strong) RNNTransitionStateHolder* content;
8 8
 @property (nonatomic, strong) RNNTransitionStateHolder* bottomTabs;
9 9
 
10
-@property (nonatomic) BOOL enable;
11
-@property (nonatomic) BOOL waitForRender;
10
+@property (nonatomic, strong) Bool* enable;
11
+@property (nonatomic, strong) Bool* waitForRender;
12 12
 
13 13
 - (BOOL)hasCustomAnimation;
14 14
 

+ 2
- 3
lib/ios/RNNScreenTransition.m View File

@@ -8,9 +8,8 @@
8 8
 	self.topBar = dict[@"topBar"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"topBar"]] : nil;
9 9
 	self.content = dict[@"content"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"content"]] : nil;
10 10
 	self.bottomTabs = dict[@"bottomTabs"] ? [[RNNTransitionStateHolder alloc] initWithDict:dict[@"bottomTabs"]] : nil;
11
-
12
-	self.enable = dict[@"enabled"] ? [dict[@"enabled"] boolValue] : YES;
13
-	self.waitForRender = dict[@"waitForRender"] ? [dict[@"waitForRender"] boolValue] : NO;
11
+	self.enable = [BoolParser parse:dict key:@"enabled"];
12
+	self.waitForRender = [BoolParser parse:dict key:@"waitForRender"];
14 13
 
15 14
 	return self;
16 15
 }

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

@@ -1,5 +1,6 @@
1 1
 #import <UIKit/UIKit.h>
2 2
 #import "RNNParentProtocol.h"
3
+#import "RNNViewControllerPresenter.h"
3 4
 
4 5
 typedef NS_ENUM(NSInteger, RNNSideMenuChildType) {
5 6
 	RNNSideMenuChildTypeCenter,

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

@@ -52,6 +52,10 @@
52 52
 	[self.options overrideOptions:options];
53 53
 }
54 54
 
55
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
56
+	[self.getCurrentChild renderTreeAndWait:wait perform:readyBlock];
57
+}
58
+
55 59
 - (void)bindChildViewController:(UIViewController<RNNLayoutProtocol>*)child {
56 60
 	self.child = child;
57 61
 	[self addChildViewController:self.child];

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

@@ -2,6 +2,7 @@
2 2
 #import "RNNSideMenuChildVC.h"
3 3
 #import "MMDrawerController.h"
4 4
 #import "RNNParentProtocol.h"
5
+#import "RNNViewControllerPresenter.h"
5 6
 
6 7
 @interface RNNSideMenuController : MMDrawerController <RNNParentProtocol>
7 8
 

+ 22
- 0
lib/ios/RNNSideMenuController.m View File

@@ -60,6 +60,26 @@
60 60
 	[self.options overrideOptions:options];
61 61
 }
62 62
 
63
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
64
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
65
+		dispatch_group_t group = dispatch_group_create();
66
+		for (UIViewController<RNNLayoutProtocol>* childViewController in self.childViewControllers) {
67
+			dispatch_group_enter(group);
68
+			dispatch_async(dispatch_get_main_queue(), ^{
69
+				[childViewController renderTreeAndWait:wait perform:^{
70
+					dispatch_group_leave(group);
71
+				}];
72
+			});
73
+		}
74
+		
75
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
76
+		
77
+		dispatch_async(dispatch_get_main_queue(), ^{
78
+			readyBlock();
79
+		});
80
+	});
81
+}
82
+
63 83
 - (void)setAnimationType:(NSString *)animationType {
64 84
 	MMDrawerControllerDrawerVisualStateBlock animationTypeStateBlock = nil;
65 85
 	if ([animationType isEqualToString:@"door"]) animationTypeStateBlock = [MMDrawerVisualState swingingDoorVisualStateBlock];
@@ -129,6 +149,8 @@
129 149
 			else if(child.type == RNNSideMenuChildTypeRight) {
130 150
 				self.right = child;
131 151
 			}
152
+			
153
+			[self addChildViewController:child];
132 154
 		}
133 155
 		
134 156
 		else {

+ 20
- 0
lib/ios/RNNSplitViewController.m View File

@@ -45,6 +45,26 @@
45 45
 	[self.options overrideOptions:options];
46 46
 }
47 47
 
48
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
49
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
50
+		dispatch_group_t group = dispatch_group_create();
51
+		for (UIViewController<RNNLayoutProtocol>* childViewController in self.childViewControllers) {
52
+			dispatch_group_enter(group);
53
+			dispatch_async(dispatch_get_main_queue(), ^{
54
+				[childViewController renderTreeAndWait:wait perform:^{
55
+					dispatch_group_leave(group);
56
+				}];
57
+			});
58
+		}
59
+		
60
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
61
+		
62
+		dispatch_async(dispatch_get_main_queue(), ^{
63
+			readyBlock();
64
+		});
65
+	});
66
+}
67
+
48 68
 - (void)bindChildViewControllers:(NSArray<UIViewController<RNNLayoutProtocol> *> *)viewControllers {
49 69
 	[self setViewControllers:viewControllers];
50 70
 	UIViewController<UISplitViewControllerDelegate>* masterViewController = viewControllers[0];

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

@@ -60,6 +60,26 @@
60 60
 	[self.options overrideOptions:options];
61 61
 }
62 62
 
63
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
64
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
65
+		dispatch_group_t group = dispatch_group_create();
66
+		for (UIViewController<RNNLayoutProtocol>* childViewController in self.childViewControllers) {
67
+			dispatch_group_enter(group);
68
+			dispatch_async(dispatch_get_main_queue(), ^{
69
+				[childViewController renderTreeAndWait:wait perform:^{
70
+					dispatch_group_leave(group);
71
+				}];
72
+			});
73
+		}
74
+		
75
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
76
+		
77
+		dispatch_async(dispatch_get_main_queue(), ^{
78
+			readyBlock();
79
+		});
80
+	});
81
+}
82
+
63 83
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
64 84
 	return self.selectedViewController.supportedInterfaceOrientations;
65 85
 }

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

@@ -12,6 +12,8 @@
12 12
 
13 13
 @interface RNNTitleViewHelper : NSObject
14 14
 
15
+@property (nonatomic, strong) RNNTitleOptions *titleOptions;
16
+@property (nonatomic, strong) RNNSubtitleOptions *subtitleOptions;
15 17
 
16 18
 - (instancetype)initWithTitleViewOptions:(RNNOptions*)titleOptions
17 19
 						 subTitleOptions:(RNNOptions*)subtitleOptions

+ 8
- 7
lib/ios/RNNTitleViewHelper.m View File

@@ -13,12 +13,7 @@
13 13
 
14 14
 @property (nonatomic, weak) UIViewController *viewController;
15 15
 
16
-@property (nonatomic, strong) NSString *title;
17
-@property (nonatomic, strong) NSString *subtitle;
18
-
19 16
 @property (nonatomic, strong) RNNTitleView *titleView;
20
-@property (nonatomic, strong) RNNTitleOptions *titleOptions;
21
-@property (nonatomic, strong) RNNSubtitleOptions *subtitleOptions;
22 17
 
23 18
 @end
24 19
 
@@ -31,8 +26,6 @@
31 26
 	self = [super init];
32 27
 	if (self) {
33 28
 		self.viewController = viewController;
34
-		self.title = [titleOptions.text getWithDefaultValue:nil];
35
-		self.subtitle = [subtitleOptions.text getWithDefaultValue:nil];
36 29
 		self.titleOptions = titleOptions;
37 30
 		self.subtitleOptions = subtitleOptions;
38 31
 		
@@ -40,6 +33,14 @@
40 33
 	return self;
41 34
 }
42 35
 
36
+- (NSString *)title {
37
+	return [self.titleOptions.text getWithDefaultValue:nil];
38
+}
39
+
40
+- (NSString *)subtitle {
41
+	return [self.subtitleOptions.text getWithDefaultValue:nil];
42
+}
43
+
43 44
 +(NSString*)validateString:(NSString*)string {
44 45
 	if ([string isEqual:[NSNull null]]) {
45 46
 		return nil;

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

@@ -1,5 +1,4 @@
1 1
 #import "RNNTopTabOptions.h"
2
-#import "RNNRootViewController.h"
3 2
 
4 3
 @implementation RNNTopTabOptions
5 4
 

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

@@ -6,7 +6,7 @@
6 6
 @property (nonatomic, retain) UIView* contentView;
7 7
 
8 8
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
9
-@property (nonatomic, retain) RNNViewControllerPresenter* presenter;
9
+@property (nonatomic, retain) RNNBasePresenter* presenter;
10 10
 @property (nonatomic, strong) RNNNavigationOptions* options;
11 11
 @property (nonatomic, strong) RNNNavigationOptions* defaultOptions;
12 12
 

+ 21
- 0
lib/ios/RNNTopTabsViewController.m View File

@@ -58,6 +58,26 @@
58 58
 	[self.options overrideOptions:options];
59 59
 }
60 60
 
61
+- (void)renderTreeAndWait:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
62
+	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
63
+		dispatch_group_t group = dispatch_group_create();
64
+		for (UIViewController<RNNLayoutProtocol>* childViewController in self.childViewControllers) {
65
+			dispatch_group_enter(group);
66
+			dispatch_async(dispatch_get_main_queue(), ^{
67
+				[childViewController renderTreeAndWait:wait perform:^{
68
+					dispatch_group_leave(group);
69
+				}];
70
+			});
71
+		}
72
+		
73
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
74
+		
75
+		dispatch_async(dispatch_get_main_queue(), ^{
76
+			readyBlock();
77
+		});
78
+	});
79
+}
80
+
61 81
 - (void)createTabBar {
62 82
 	_segmentedControl = [[RNNSegmentedControl alloc] initWithSectionTitles:@[@"", @"", @""]];
63 83
 	_segmentedControl.frame = CGRectMake(0, 0, self.view.bounds.size.width, 50);
@@ -91,6 +111,7 @@
91 111
 	for (RNNRootViewController* childVc in viewControllers) {
92 112
 		[childVc.view setFrame:_contentView.bounds];
93 113
 //		[childVc.options.topTab applyOn:childVc];
114
+		[self addChildViewController:childVc];
94 115
 	}
95 116
 	
96 117
 	[self setSelectedViewControllerIndex:0];

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

@@ -7,7 +7,9 @@
7 7
 @property (nonatomic, strong) RNNScreenTransition* push;
8 8
 @property (nonatomic, strong) RNNScreenTransition* pop;
9 9
 @property (nonatomic, strong) RNNScreenTransition* showModal;
10
+@property (nonatomic, strong) RNNScreenTransition* showOverlay;
10 11
 @property (nonatomic, strong) RNNScreenTransition* dismissModal;
11 12
 @property (nonatomic, strong) RNNScreenTransition* setStackRoot;
13
+@property (nonatomic, strong) RNNScreenTransition* setRoot;
12 14
 
13 15
 @end

+ 2
- 0
lib/ios/RNNTransitionsOptions.m View File

@@ -8,8 +8,10 @@
8 8
 	self.push = [[RNNScreenTransition alloc] initWithDict:dict[@"push"]];
9 9
 	self.pop = [[RNNScreenTransition alloc] initWithDict:dict[@"pop"]];
10 10
 	self.showModal = [[RNNScreenTransition alloc] initWithDict:dict[@"showModal"]];
11
+	self.showOverlay = [[RNNScreenTransition alloc] initWithDict:dict[@"showOverlay"]];
11 12
 	self.dismissModal = [[RNNScreenTransition alloc] initWithDict:dict[@"dismissModal"]];
12 13
 	self.setStackRoot = [[RNNScreenTransition alloc] initWithDict:dict[@"setStackRoot"]];
14
+	self.setRoot = [[RNNScreenTransition alloc] initWithDict:dict[@"setRoot"]];
13 15
 
14 16
 	return self;
15 17
 }

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

@@ -1,10 +1,13 @@
1 1
 #import "RNNBasePresenter.h"
2 2
 #import "RNNNavigationButtons.h"
3
+#import "RNNReactComponentManager.h"
3 4
 
4 5
 @interface RNNViewControllerPresenter : RNNBasePresenter
5 6
 
6
-@property (nonatomic, strong) RNNNavigationButtons* navigationButtons;
7
+- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager;
8
+
9
+- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
7 10
 
8
-- (void)bindViewController:(UIViewController *)bindedViewController viewCreator:(id<RNNRootViewCreator>)creator;
11
+@property (nonatomic, strong) RNNNavigationButtons* navigationButtons;
9 12
 
10 13
 @end

+ 94
- 3
lib/ios/RNNViewControllerPresenter.m View File

@@ -3,12 +3,38 @@
3 3
 #import "UITabBarController+RNNOptions.h"
4 4
 #import "RCTConvert+Modal.h"
5 5
 #import "RNNReactView.h"
6
+#import "RNNCustomTitleView.h"
7
+#import "RNNTitleViewHelper.h"
8
+
9
+@interface RNNViewControllerPresenter() {
10
+	RNNReactView* _customTitleView;
11
+	RNNTitleViewHelper* _titleViewHelper;
12
+	RNNReactComponentManager* _componentManager;
13
+}
14
+
15
+@end
6 16
 
7 17
 @implementation RNNViewControllerPresenter
8 18
 
9
-- (void)bindViewController:(UIViewController *)bindedViewController viewCreator:(id<RNNRootViewCreator>)creator {
19
+- (instancetype)init {
20
+	self = [super init];
21
+	[[NSNotificationCenter defaultCenter] addObserver:self
22
+											 selector:@selector(cleanReactLeftovers)
23
+												 name:RCTJavaScriptWillStartLoadingNotification
24
+											   object:nil];
25
+	
26
+	return self;
27
+}
28
+
29
+- (instancetype)initWithComponentManager:(RNNReactComponentManager *)componentManager {
30
+	self = [self init];
31
+	_componentManager = componentManager;
32
+	return self;
33
+}
34
+
35
+- (void)bindViewController:(UIViewController *)bindedViewController {
10 36
 	self.bindedViewController = bindedViewController;
11
-	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController rootViewCreator:creator];
37
+	_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:self.bindedViewController componentManager:_componentManager];
12 38
 }
13 39
 
14 40
 - (void)applyOptions:(RNNNavigationOptions *)options {
@@ -23,7 +49,7 @@
23 49
 	[viewController rnn_setStatusBarStyle:[options.statusBar.style getWithDefaultValue:@"default"] animated:[options.statusBar.animate getWithDefaultValue:YES]];
24 50
 	[viewController rnn_setBackButtonVisible:[options.topBar.backButton.visible getWithDefaultValue:YES]];
25 51
 	[viewController rnn_setInterceptTouchOutside:[options.overlay.interceptTouchOutside getWithDefaultValue:YES]];
26
-
52
+	
27 53
 	if (options.layout.backgroundColor.hasValue) {
28 54
 		[viewController rnn_setBackgroundColor:options.layout.backgroundColor.get];
29 55
 	}
@@ -35,6 +61,8 @@
35 61
 		}
36 62
 		[viewController rnn_setSearchBarWithPlaceholder:[options.topBar.searchBarPlaceholder getWithDefaultValue:@""] hideNavBarOnFocusSearchBar: hideNavBarOnFocusSearchBar];
37 63
 	}
64
+	
65
+	[self setTitleViewWithSubtitle:options];
38 66
 }
39 67
 
40 68
 - (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
@@ -125,6 +153,69 @@
125 153
 		RCTRootView* rootView = (RCTRootView*)viewController.view;
126 154
 		rootView.passThroughTouches = !newOptions.overlay.interceptTouchOutside.get;
127 155
 	}
156
+	
157
+	[self setTitleViewWithSubtitle:newOptions];
158
+	
159
+	if (newOptions.topBar.title.component.name.hasValue) {
160
+		[self setCustomNavigationTitleView:newOptions perform:nil];
161
+	}
162
+}
163
+
164
+- (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
165
+	[self setCustomNavigationTitleView:options perform:readyBlock];
166
+}
167
+
168
+- (void)setCustomNavigationTitleView:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock {
169
+	UIViewController<RNNLayoutProtocol>* viewController = self.bindedViewController;
170
+	if (![options.topBar.title.component.waitForRender getWithDefaultValue:NO] && readyBlock) {
171
+		readyBlock();
172
+		readyBlock = nil;
173
+	}
174
+	
175
+	if (options.topBar.title.component.name.hasValue) {
176
+		_customTitleView = (RNNReactView*)[_componentManager createComponentIfNotExists:options.topBar.title.component parentComponentId:viewController.layoutInfo.componentId reactViewReadyBlock:readyBlock];
177
+		_customTitleView.backgroundColor = UIColor.clearColor;
178
+		NSString* alignment = [options.topBar.title.component.alignment getWithDefaultValue:@""];
179
+		[_customTitleView setAlignment:alignment];
180
+		BOOL isCenter = [alignment isEqualToString:@"center"];
181
+		__weak RNNReactView *weakTitleView = _customTitleView;
182
+		CGRect frame = viewController.navigationController.navigationBar.bounds;
183
+		[_customTitleView setFrame:frame];
184
+		[_customTitleView setRootViewDidChangeIntrinsicSize:^(CGSize intrinsicContentSize) {
185
+			if (isCenter) {
186
+				[weakTitleView setFrame:CGRectMake(0, 0, intrinsicContentSize.width, intrinsicContentSize.height)];
187
+			} else {
188
+				[weakTitleView setFrame:frame];
189
+			}
190
+		}];
191
+		
192
+		viewController.navigationItem.titleView = _customTitleView;
193
+	} else {
194
+		[_customTitleView removeFromSuperview];
195
+		if (readyBlock) {
196
+			readyBlock();
197
+		}
198
+	}
199
+}
200
+
201
+- (void)setTitleViewWithSubtitle:(RNNNavigationOptions *)options {
202
+	if (!_customTitleView && options.topBar.subtitle.text.hasValue) {
203
+		_titleViewHelper = [[RNNTitleViewHelper alloc] initWithTitleViewOptions:options.topBar.title subTitleOptions:options.topBar.subtitle viewController:self.bindedViewController];
204
+		[_titleViewHelper setup];
205
+	} else if (_titleViewHelper) {
206
+		if (options.topBar.title.text.hasValue) {
207
+			[_titleViewHelper setTitleOptions:options.topBar.title];
208
+		}
209
+		if (options.topBar.subtitle.text.hasValue) {
210
+			[_titleViewHelper setSubtitleOptions:options.topBar.subtitle];
211
+		}
212
+		
213
+		[_titleViewHelper setup];
214
+	}
215
+}
216
+
217
+- (void)cleanReactLeftovers {
218
+	_customTitleView = nil;
128 219
 }
129 220
 
130 221
 @end

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

@@ -93,8 +93,6 @@
93 93
 		501E0217213E7EA3003365C5 /* RNNReactView.h in Headers */ = {isa = PBXBuildFile; fileRef = 501E0215213E7EA3003365C5 /* RNNReactView.h */; };
94 94
 		501E0218213E7EA3003365C5 /* RNNReactView.m in Sources */ = {isa = PBXBuildFile; fileRef = 501E0216213E7EA3003365C5 /* RNNReactView.m */; };
95 95
 		50206A6D21AFE75400B7BB1A /* RNNSideMenuParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 50206A6C21AFE75400B7BB1A /* RNNSideMenuParserTest.m */; };
96
-		50220F48212ABDFD004C2B0A /* RNNReactRootView.h in Headers */ = {isa = PBXBuildFile; fileRef = 50220F46212ABDFD004C2B0A /* RNNReactRootView.h */; };
97
-		50220F49212ABDFD004C2B0A /* RNNReactRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50220F47212ABDFD004C2B0A /* RNNReactRootView.m */; };
98 96
 		502CB46E20CD1DDA0019B2FE /* RNNBackButtonOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */; };
99 97
 		502CB46F20CD1DDA0019B2FE /* RNNBackButtonOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */; };
100 98
 		502F0E142178CF8200367CC3 /* UIViewController+RNNOptionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 502F0E132178CF8200367CC3 /* UIViewController+RNNOptionsTest.m */; };
@@ -168,6 +166,8 @@
168 166
 		504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */; };
169 167
 		504AFE761FFFF1E00076E904 /* RNNNavigationOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */; };
170 168
 		504AFE771FFFF1E20076E904 /* RNNTopBarOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */; };
169
+		5050465421F8F4490035497A /* RNNReactComponentManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 5050465221F8F4490035497A /* RNNReactComponentManager.h */; };
170
+		5050465521F8F4490035497A /* RNNReactComponentManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5050465321F8F4490035497A /* RNNReactComponentManager.m */; };
171 171
 		5053CE7F2175FB1900D0386B /* RNNDefaultOptionsHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 5053CE7D2175FB1900D0386B /* RNNDefaultOptionsHelper.h */; };
172 172
 		5053CE802175FB1900D0386B /* RNNDefaultOptionsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 5053CE7E2175FB1900D0386B /* RNNDefaultOptionsHelper.m */; };
173 173
 		50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50570B242061473D006A1B5C /* RNNTitleOptions.h */; };
@@ -421,8 +421,6 @@
421 421
 		501E0215213E7EA3003365C5 /* RNNReactView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactView.h; sourceTree = "<group>"; };
422 422
 		501E0216213E7EA3003365C5 /* RNNReactView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactView.m; sourceTree = "<group>"; };
423 423
 		50206A6C21AFE75400B7BB1A /* RNNSideMenuParserTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSideMenuParserTest.m; sourceTree = "<group>"; };
424
-		50220F46212ABDFD004C2B0A /* RNNReactRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactRootView.h; sourceTree = "<group>"; };
425
-		50220F47212ABDFD004C2B0A /* RNNReactRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactRootView.m; sourceTree = "<group>"; };
426 424
 		502CB43920CBCA140019B2FE /* RNNBridgeManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBridgeManagerDelegate.h; sourceTree = "<group>"; };
427 425
 		502CB46C20CD1DDA0019B2FE /* RNNBackButtonOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNBackButtonOptions.h; sourceTree = "<group>"; };
428 426
 		502CB46D20CD1DDA0019B2FE /* RNNBackButtonOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNBackButtonOptions.m; sourceTree = "<group>"; };
@@ -496,6 +494,8 @@
496 494
 		504AFE631FFE53070076E904 /* RNNOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNOptions.m; sourceTree = "<group>"; };
497 495
 		504AFE721FFFF0540076E904 /* RNNTopTabsOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTopTabsOptions.h; sourceTree = "<group>"; };
498 496
 		504AFE731FFFF0540076E904 /* RNNTopTabsOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNTopTabsOptions.m; sourceTree = "<group>"; };
497
+		5050465221F8F4490035497A /* RNNReactComponentManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNReactComponentManager.h; sourceTree = "<group>"; };
498
+		5050465321F8F4490035497A /* RNNReactComponentManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNReactComponentManager.m; sourceTree = "<group>"; };
499 499
 		5053CE7D2175FB1900D0386B /* RNNDefaultOptionsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNDefaultOptionsHelper.h; sourceTree = "<group>"; };
500 500
 		5053CE7E2175FB1900D0386B /* RNNDefaultOptionsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNDefaultOptionsHelper.m; sourceTree = "<group>"; };
501 501
 		50570B242061473D006A1B5C /* RNNTitleOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNTitleOptions.h; sourceTree = "<group>"; };
@@ -972,6 +972,8 @@
972 972
 				261F0E691E6F028A00989DE2 /* RNNNavigationStackManager.m */,
973 973
 				50D031322005149000386B3D /* RNNOverlayManager.h */,
974 974
 				50D031332005149000386B3D /* RNNOverlayManager.m */,
975
+				5050465221F8F4490035497A /* RNNReactComponentManager.h */,
976
+				5050465321F8F4490035497A /* RNNReactComponentManager.m */,
975 977
 			);
976 978
 			name = Managers;
977 979
 			sourceTree = "<group>";
@@ -990,8 +992,6 @@
990 992
 				50570BE82063E09B006A1B5C /* RNNTitleViewHelper.h */,
991 993
 				501CD31D214A5B6900A6E225 /* RNNLayoutInfo.h */,
992 994
 				501CD31E214A5B6900A6E225 /* RNNLayoutInfo.m */,
993
-				50220F46212ABDFD004C2B0A /* RNNReactRootView.h */,
994
-				50220F47212ABDFD004C2B0A /* RNNReactRootView.m */,
995 995
 				501E0215213E7EA3003365C5 /* RNNReactView.h */,
996 996
 				501E0216213E7EA3003365C5 /* RNNReactView.m */,
997 997
 				50570BE92063E09B006A1B5C /* RNNTitleViewHelper.m */,
@@ -1279,6 +1279,7 @@
1279 1279
 				5012242221736883000F5F98 /* NullColor.h in Headers */,
1280 1280
 				50570B262061473D006A1B5C /* RNNTitleOptions.h in Headers */,
1281 1281
 				50395593217485B000B0A663 /* Double.h in Headers */,
1282
+				5050465421F8F4490035497A /* RNNReactComponentManager.h in Headers */,
1282 1283
 				504AFE741FFFF0540076E904 /* RNNTopTabsOptions.h in Headers */,
1283 1284
 				E8E5182E1F83A48B000467AC /* RNNTransitionStateHolder.h in Headers */,
1284 1285
 				5049594A216F5FE6006D2B81 /* NullText.h in Headers */,
@@ -1286,7 +1287,6 @@
1286 1287
 				5038A3CA216E328A009280BC /* Param.h in Headers */,
1287 1288
 				50887CAA20F26BFE00D06111 /* RNNOverlayWindow.h in Headers */,
1288 1289
 				507E7D57201DDD3000444E6C /* RNNAnimationOptions.h in Headers */,
1289
-				50220F48212ABDFD004C2B0A /* RNNReactRootView.h in Headers */,
1290 1290
 				5053CE7F2175FB1900D0386B /* RNNDefaultOptionsHelper.h in Headers */,
1291 1291
 				2DCD9195200014A900EDC75D /* RNNBridgeManager.h in Headers */,
1292 1292
 				7B1126A91E2D2B6C00F9B03B /* RNNControllerFactory.h in Headers */,
@@ -1462,6 +1462,7 @@
1462 1462
 				5012242721737278000F5F98 /* NullImage.m in Sources */,
1463 1463
 				7B1126A01E2D263F00F9B03B /* RNNEventEmitter.m in Sources */,
1464 1464
 				A7626BFD1FC2FB2C00492FB8 /* RNNTopBarOptions.m in Sources */,
1465
+				5050465521F8F4490035497A /* RNNReactComponentManager.m in Sources */,
1465 1466
 				263905CB1E4C6F440023D7D3 /* SidebarLuvocracyAnimation.m in Sources */,
1466 1467
 				50570BEB2063E09B006A1B5C /* RNNTitleViewHelper.m in Sources */,
1467 1468
 				263905E71E4CAC950023D7D3 /* RNNSideMenuChildVC.m in Sources */,
@@ -1509,7 +1510,6 @@
1509 1510
 				50EB4ED82068EBE000D6ED34 /* RNNBackgroundOptions.m in Sources */,
1510 1511
 				507F43CA1FF4F9CC00D9425B /* RNNTopTabOptions.m in Sources */,
1511 1512
 				26916C991E4B9E7700D13680 /* RNNReactRootViewCreator.m in Sources */,
1512
-				50220F49212ABDFD004C2B0A /* RNNReactRootView.m in Sources */,
1513 1513
 				5064495E20DC62B90026709C /* RNNSideMenuSideOptions.m in Sources */,
1514 1514
 				214545251F4DC125006E8DA1 /* RNNUIBarButtonItem.m in Sources */,
1515 1515
 				263905B81E4C6F440023D7D3 /* UIViewController+MMDrawerController.m in Sources */,

+ 38
- 11
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

@@ -62,7 +62,7 @@
62 62
 	self.store = [OCMockObject partialMockForObject:[[RNNStore alloc] init]];
63 63
 	self.eventEmmiter = [OCMockObject partialMockForObject:[RNNEventEmitter new]];
64 64
 	self.overlayManager = [OCMockObject partialMockForObject:[RNNOverlayManager new]];
65
-	self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter andBridge:nil]];
65
+	self.controllerFactory = [OCMockObject partialMockForObject:[[RNNControllerFactory alloc] initWithRootViewCreator:nil eventEmitter:self.eventEmmiter store:self.store componentManager:nil andBridge:nil]];
66 66
 	self.uut = [[RNNCommandsHandler alloc] initWithStore:self.store controllerFactory:self.controllerFactory eventEmitter:self.eventEmmiter stackManager:[RNNNavigationStackManager new] modalManager:[RNNModalManager new] overlayManager:self.overlayManager mainWindow:_mainWindow];
67 67
 	self.vc1 = [RNNRootViewController new];
68 68
 	self.vc2 = [RNNRootViewController new];
@@ -126,7 +126,7 @@
126 126
 	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
127 127
 	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:creator eventEmitter:nil presenter:presenter options:initialOptions defaultOptions:nil];
128 128
 	
129
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[vc] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
129
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:creator childViewControllers:@[vc] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
130 130
 	
131 131
 	[vc viewWillAppear:false];
132 132
 	XCTAssertTrue([vc.navigationItem.title isEqual:@"the title"]);
@@ -213,7 +213,7 @@
213 213
 	OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]);
214 214
 	NSDictionary* layout = @{};
215 215
 	
216
-	[[self.controllerFactory expect] createLayout:layout saveToStore:self.store];
216
+	[[self.controllerFactory expect] createLayout:layout];
217 217
 	[self.uut showOverlay:layout completion:^{}];
218 218
 	[self.controllerFactory verify];
219 219
 }
@@ -221,9 +221,9 @@
221 221
 - (void)testShowOverlay_saveToStore {
222 222
 	[self.store setReadyToReceiveCommands:true];
223 223
 	OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]);
224
-	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]);
224
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]);
225 225
 	
226
-	[[self.controllerFactory expect] createLayout:[OCMArg any] saveToStore:self.store];
226
+	[[self.controllerFactory expect] createLayout:[OCMArg any]];
227 227
 	[self.uut showOverlay:@{} completion:^{}];
228 228
 	[self.overlayManager verify];
229 229
 }
@@ -231,7 +231,7 @@
231 231
 - (void)testShowOverlay_withCreatedLayout {
232 232
 	[self.store setReadyToReceiveCommands:true];
233 233
 	UIViewController* layoutVC = [RNNRootViewController new];
234
-	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]).andReturn(layoutVC);
234
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(layoutVC);
235 235
 	
236 236
 	[[self.overlayManager expect] showOverlayWindow:[OCMArg any]];
237 237
 	[self.uut showOverlay:@{} completion:^{}];
@@ -241,7 +241,8 @@
241 241
 - (void)testShowOverlay_invokeNavigationCommandEventWithLayout {
242 242
 	[self.store setReadyToReceiveCommands:true];
243 243
 	OCMStub([self.overlayManager showOverlayWindow:[OCMArg any]]);
244
-	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:[OCMArg any]]);
244
+	id mockedVC = [OCMockObject partialMockForObject:self.vc1];	
245
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC);
245 246
 	
246 247
 	NSDictionary* layout = @{};
247 248
 	
@@ -294,7 +295,7 @@
294 295
 
295 296
 - (void)testSetRoot_setRootViewControllerOnMainWindow {
296 297
 	[self.store setReadyToReceiveCommands:true];
297
-	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:self.store]).andReturn(self.vc1);
298
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(self.vc1);
298 299
 	
299 300
 	[[self.mainWindow expect] setRootViewController:self.vc1];
300 301
 	[self.uut setRoot:@{} completion:^{}];
@@ -303,7 +304,7 @@
303 304
 
304 305
 - (void)testSetRoot_removeAllComponentsFromMainWindow {
305 306
 	[self.store setReadyToReceiveCommands:true];
306
-	OCMStub([self.controllerFactory createLayout:[OCMArg any] saveToStore:self.store]).andReturn(self.vc1);
307
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(self.vc1);
307 308
 	
308 309
 	[[self.store expect] removeAllComponentsFromWindow:self.mainWindow];
309 310
 	[self.uut setRoot:@{} completion:^{}];
@@ -311,7 +312,7 @@
311 312
 }
312 313
 
313 314
 - (void)testSetStackRoot_resetStackWithSingleComponent {
314
-	OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any] saveToStore:self.store]).andReturn(@[self.vc2]);
315
+	OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any]]).andReturn(@[self.vc2]);
315 316
 	[self.store setReadyToReceiveCommands:true];
316 317
 	[self.uut setStackRoot:@"vc1" children:nil completion:^{
317 318
 		
@@ -324,7 +325,7 @@
324 325
 
325 326
 - (void)testSetStackRoot_setMultipleChildren {
326 327
 	NSArray* newViewControllers = @[_vc1, _vc3];
327
-	OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any] saveToStore:self.store]).andReturn(newViewControllers);
328
+	OCMStub([self.controllerFactory createChildrenLayout:[OCMArg any]]).andReturn(newViewControllers);
328 329
 	[self.store setReadyToReceiveCommands:true];
329 330
 	[self.uut setStackRoot:@"vc1" children:nil completion:^{
330 331
 		
@@ -334,4 +335,30 @@
334 335
 	XCTAssertTrue([_nvc.viewControllers isEqual:newViewControllers]);
335 336
 }
336 337
 
338
+- (void)testSetRoot_waitForRenderTrue {
339
+	[self.store setReadyToReceiveCommands:true];
340
+	self.vc1.options = [[RNNNavigationOptions alloc] initEmptyOptions];
341
+	self.vc1.options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:YES];
342
+	
343
+	id mockedVC = [OCMockObject partialMockForObject:self.vc1];
344
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC);
345
+	
346
+	[[mockedVC expect] renderTreeAndWait:YES perform:[OCMArg any]];
347
+	[self.uut setRoot:@{} completion:^{}];
348
+	[mockedVC verify];
349
+}
350
+
351
+- (void)testSetRoot_waitForRenderFalse {
352
+	[self.store setReadyToReceiveCommands:true];
353
+	self.vc1.options = [[RNNNavigationOptions alloc] initEmptyOptions];
354
+	self.vc1.options.animations.setRoot.waitForRender = [[Bool alloc] initWithBOOL:NO];
355
+	
356
+	id mockedVC = [OCMockObject partialMockForObject:self.vc1];
357
+	OCMStub([self.controllerFactory createLayout:[OCMArg any]]).andReturn(mockedVC);
358
+	
359
+	[[mockedVC expect] renderTreeAndWait:NO perform:[OCMArg any]];
360
+	[self.uut setRoot:@{} completion:^{}];
361
+	[mockedVC verify];
362
+}
363
+
337 364
 @end

+ 11
- 11
lib/ios/ReactNativeNavigationTests/RNNControllerFactoryTest.m View File

@@ -23,7 +23,7 @@
23 23
 	[super setUp];
24 24
 	self.creator = nil;
25 25
 	self.store = [RNNStore new];
26
-	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator eventEmitter:nil andBridge:nil];
26
+	self.factory = [[RNNControllerFactory alloc] initWithRootViewCreator:self.creator eventEmitter:nil store:self.store componentManager:nil andBridge:nil];
27 27
 }
28 28
 
29 29
 - (void)tearDown {
@@ -31,7 +31,7 @@
31 31
 }
32 32
 
33 33
 - (void)testCreateLayout_EmptyLayout {
34
-	XCTAssertThrows([self.factory createLayout:@{} saveToStore:self.store]);
34
+	XCTAssertThrows([self.factory createLayout:@{}]);
35 35
 }
36 36
 
37 37
 - (void)testCreateLayout_ComponentLayout {
@@ -39,7 +39,7 @@
39 39
 							 @"type": @"Component",
40 40
 							 @"data": @{},
41 41
 							 @"children": @[]};
42
-	id ans = [self.factory createLayout:layout saveToStore:self.store];
42
+	id ans = [self.factory createLayout:layout];
43 43
 	XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]);
44 44
 }
45 45
 
@@ -52,7 +52,7 @@
52 52
 							  @"type": @"ExternalComponent",
53 53
 							  @"data": @{@"name": @"externalComponent"},
54 54
 							  @"children": @[]};
55
-	id ans = [self.factory createLayout:layout saveToStore:self.store];
55
+	id ans = [self.factory createLayout:layout];
56 56
 	XCTAssertTrue([ans isMemberOfClass:[RNNRootViewController class]]);
57 57
 }
58 58
 
@@ -61,7 +61,7 @@
61 61
 							 @"type": @"Stack",
62 62
 							 @"data": @{},
63 63
 							 @"children": @[]};
64
-	id ans = [self.factory createLayout:layout saveToStore:self.store];
64
+	id ans = [self.factory createLayout:layout];
65 65
 	XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]);
66 66
 }
67 67
 
@@ -78,7 +78,7 @@
78 78
 									   @"type": @"Component",
79 79
 									   @"data": @{},
80 80
 									   @"children": @[]}]};
81
-	id ans = [self.factory createLayout:layout saveToStore:self.store];
81
+	id ans = [self.factory createLayout:layout];
82 82
 	XCTAssertTrue([ans isMemberOfClass:[RNNSplitViewController class]]);
83 83
 }
84 84
 
@@ -91,7 +91,7 @@
91 91
 									   @"type": @"Component",
92 92
 									   @"data": @{},
93 93
 									   @"children": @[]}]};
94
-	RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayout:layout saveToStore:self.store];
94
+	RNNNavigationController* ans = (RNNNavigationController*) [self.factory createLayout:layout];
95 95
 	
96 96
 	XCTAssertTrue([ans isMemberOfClass:[RNNNavigationController class]]);
97 97
 	XCTAssertTrue(ans.childViewControllers.count == 1);
@@ -112,7 +112,7 @@
112 112
 												 @"type": @"Component",
113 113
 												 @"data": @{},
114 114
 												 @"children": @[]}]}]};
115
-	RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayout:layout saveToStore:self.store];
115
+	RNNTabBarController* tabBar = (RNNTabBarController*) [self.factory createLayout:layout];
116 116
 	
117 117
 	XCTAssertTrue([tabBar isMemberOfClass:[RNNTabBarController class]]);
118 118
 	XCTAssertTrue(tabBar.childViewControllers.count == 1);
@@ -137,7 +137,7 @@
137 137
 												 @"type": @"Component",
138 138
 												 @"data": @{},
139 139
 												 @"children": @[]}]}]};
140
-	RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayout:layout saveToStore:self.store];
140
+	RNNTopTabsViewController* tabBar = (RNNTopTabsViewController*) [self.factory createLayout:layout];
141 141
 	
142 142
 	XCTAssertTrue([tabBar isMemberOfClass:[RNNTopTabsViewController class]]);
143 143
 }
@@ -171,7 +171,7 @@
171 171
 												 @"type": @"Component",
172 172
 												 @"data": @{},
173 173
 												 @"children": @[]}]}]};
174
-	RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayout:layout saveToStore:self.store];
174
+	RNNSideMenuController *ans = (RNNSideMenuController*) [self.factory createLayout:layout];
175 175
 	XCTAssertTrue([ans isMemberOfClass:[RNNSideMenuController class]]);
176 176
 	XCTAssertTrue([ans isKindOfClass:[UIViewController class]]);
177 177
 	XCTAssertTrue([ans.center isMemberOfClass:[RNNSideMenuChildVC class]]);
@@ -194,7 +194,7 @@
194 194
 							 @"type": @"Component",
195 195
 							 @"data": @{},
196 196
 							 @"children": @[]};
197
-	UIViewController *ans = [self.factory createLayout:layout saveToStore:self.store];
197
+	UIViewController *ans = [self.factory createLayout:layout];
198 198
 	
199 199
 	UIViewController *storeAns = [self.store findComponentForId:componentId];
200 200
 	XCTAssertEqualObjects(ans, storeAns);

+ 6
- 4
lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m View File

@@ -2,6 +2,7 @@
2 2
 #import <OCMock/OCMock.h>
3 3
 #import "RNNNavigationController.h"
4 4
 #import "RNNRootViewController.h"
5
+#import "RNNTestRootViewCreator.h"
5 6
 
6 7
 @interface RNNNavigationControllerTest : XCTestCase
7 8
 
@@ -15,17 +16,18 @@
15 16
 	RNNRootViewController* _vc2;
16 17
 	UIViewController* _vc3;
17 18
 	RNNNavigationOptions* _options;
19
+	RNNTestRootViewCreator* _creator;
18 20
 }
19 21
 
20 22
 - (void)setUp {
21 23
     [super setUp];
22
-	
24
+	_creator = [[RNNTestRootViewCreator alloc] init];
23 25
 	_vc1 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[OCMockObject partialMockForObject:[[RNNViewControllerPresenter alloc] init]] options:[[RNNNavigationOptions alloc] initEmptyOptions]  defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
24 26
 	_vc2 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNViewControllerPresenter alloc] init] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:[[RNNNavigationOptions alloc] initEmptyOptions]];
25 27
 	_vc2Mock = [OCMockObject partialMockForObject:_vc2];
26 28
 	_vc3 = [UIViewController new];
27 29
 	_options = [OCMockObject partialMockForObject:[[RNNNavigationOptions alloc] initEmptyOptions]];
28
-	self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1, _vc2] options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNNavigationControllerPresenter alloc] init]]];
30
+	self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1, _vc2] options:_options defaultOptions:nil presenter:[OCMockObject partialMockForObject:[[RNNNavigationControllerPresenter alloc] init]]];
29 31
 }
30 32
 
31 33
 - (void)testInitWithLayoutInfo_shouldBindPresenter {
@@ -33,7 +35,7 @@
33 35
 }
34 36
 
35 37
 - (void)testInitWithLayoutInfo_shouldSetMultipleViewControllers {
36
-	self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1, _vc2] options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init]];
38
+	self.uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1, _vc2] options:[[RNNNavigationOptions alloc] initWithDict:@{}] defaultOptions:nil presenter:[[RNNViewControllerPresenter alloc] init]];
37 39
 	XCTAssertTrue(self.uut.viewControllers.count == 2);
38 40
 }
39 41
 
@@ -157,7 +159,7 @@
157 159
 }
158 160
 
159 161
 - (RNNNavigationController *)createNavigationControllerWithOptions:(RNNNavigationOptions *)options {
160
-	return [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[_vc1] options:options defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
162
+	return [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[_vc1] options:options defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
161 163
 }
162 164
 
163 165
 @end

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

@@ -415,7 +415,7 @@
415 415
 -(void)testRightButtonsWithTitle_withoutStyle {
416 416
 	self.options.topBar.rightButtons = @[@{@"id": @"testId", @"text": @"test"}];
417 417
 	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil];
418
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
418
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
419 419
 
420 420
 	RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.rightBarButtonItems objectAtIndex:0];
421 421
 	NSString* expectedButtonId = @"testId";
@@ -430,7 +430,7 @@
430 430
 
431 431
 	self.options.topBar.rightButtons = @[@{@"id": @"testId", @"text": @"test", @"enabled": @false, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
432 432
 	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil];
433
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
433
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
434 434
 
435 435
 	RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.rightBarButtonItems objectAtIndex:0];
436 436
 	NSString* expectedButtonId = @"testId";
@@ -446,7 +446,7 @@
446 446
 -(void)testLeftButtonsWithTitle_withoutStyle {
447 447
 	self.options.topBar.leftButtons = @[@{@"id": @"testId", @"text": @"test"}];
448 448
 	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil];
449
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
449
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
450 450
 
451 451
 	RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.leftBarButtonItems objectAtIndex:0];
452 452
 	NSString* expectedButtonId = @"testId";
@@ -461,7 +461,7 @@
461 461
 
462 462
 	self.options.topBar.leftButtons = @[@{@"id": @"testId", @"text": @"test", @"enabled": @false, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
463 463
 	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[RNNViewControllerPresenter new] options:self.options defaultOptions:nil];
464
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
464
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:_creator childViewControllers:@[self.uut] options:nil defaultOptions:nil presenter:nil];
465 465
 
466 466
 	RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.leftBarButtonItems objectAtIndex:0];
467 467
 	NSString* expectedButtonId = @"testId";
@@ -565,7 +565,7 @@
565 565
 
566 566
 
567 567
 - (RNNNavigationController *)createNavigationController {
568
-	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil childViewControllers:@[self.uut] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
568
+	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:nil childViewControllers:@[self.uut] options:[[RNNNavigationOptions alloc] initEmptyOptions] defaultOptions:nil presenter:[[RNNNavigationControllerPresenter alloc] init]];
569 569
 	
570 570
 	return nav;
571 571
 }

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

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

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

@@ -70,7 +70,7 @@
70 70
 
71 71
 - (void)testBindViewControllerShouldCreateNavigationButtonsCreator {
72 72
 	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
73
-	[presenter bindViewController:self.bindedViewController viewCreator:nil];
73
+	[presenter bindViewController:self.bindedViewController];
74 74
 	XCTAssertNotNil(presenter.navigationButtons);
75 75
 }
76 76
 

+ 1
- 0
lib/ios/UIViewController+RNNOptions.m View File

@@ -43,6 +43,7 @@ const NSInteger BLUR_STATUS_TAG = 78264801;
43 43
 			}
44 44
 			search.hidesNavigationBarDuringPresentation = hideNavBarOnFocusSearchBar;
45 45
 			self.navigationItem.searchController = search;
46
+			[self.navigationItem setHidesSearchBarWhenScrolling:NO];
46 47
 			
47 48
 			// Fixes #3450, otherwise, UIKit will infer the presentation context to be the root most view controller
48 49
 			self.definesPresentationContext = YES;

+ 2
- 1
playground/src/app.js View File

@@ -50,7 +50,8 @@ function start() {
50 50
             from: 0,
51 51
             to: 1,
52 52
             duration: 300
53
-          }
53
+          },
54
+          waitForRender: true
54 55
         },
55 56
         _push: {
56 57
           topBar: {