Browse Source

Detached options from presenters, design improvments for LayoutProtocol (#4049)

* Detached options from presenters, design improvments for LayoutProtocol

* fixes topBar.background.clipToBounds

* Fixes e2e

* Fixes unit tests

* Homogeneous constructors for parent and child view controllers

* Handle parent options merging with children in optionsResolver

* Fix topBar.background.clipToBounds

* Removed unnecessary options merging

* Fixes shared element transition

* Fixed sideMenu inside bottomTabs
Yogev Ben David 6 years ago
parent
commit
f7a0e5679d
No account linked to committer's email address
38 changed files with 455 additions and 471 deletions
  1. 1
    1
      e2e/ComplexLayout.test.js
  2. 6
    0
      lib/ios/RNNBackgroundOptions.m
  3. 2
    18
      lib/ios/RNNBasePresenter.h
  4. 3
    22
      lib/ios/RNNBasePresenter.m
  5. 30
    26
      lib/ios/RNNCommandsHandler.m
  6. 55
    71
      lib/ios/RNNControllerFactory.m
  7. 3
    0
      lib/ios/RNNLayoutProtocol.h
  8. 3
    1
      lib/ios/RNNLeafProtocol.h
  9. 2
    2
      lib/ios/RNNModalManager.m
  10. 3
    0
      lib/ios/RNNNavigationController.h
  11. 25
    17
      lib/ios/RNNNavigationController.m
  12. 2
    1
      lib/ios/RNNNavigationControllerPresenter.m
  13. 0
    2
      lib/ios/RNNNavigationOptions.h
  14. 0
    8
      lib/ios/RNNNavigationOptions.m
  15. 8
    0
      lib/ios/RNNParentOptionsResolver.h
  16. 9
    0
      lib/ios/RNNParentOptionsResolver.m
  17. 5
    2
      lib/ios/RNNParentProtocol.h
  18. 13
    6
      lib/ios/RNNRootViewController.h
  19. 58
    59
      lib/ios/RNNRootViewController.m
  20. 0
    19
      lib/ios/RNNSideMenu/MMDrawerController/MMDrawerController.m
  21. 3
    9
      lib/ios/RNNSideMenuChildVC.h
  22. 28
    30
      lib/ios/RNNSideMenuChildVC.m
  23. 2
    2
      lib/ios/RNNSideMenuController.h
  24. 18
    23
      lib/ios/RNNSideMenuController.m
  25. 15
    2
      lib/ios/RNNSideMenuSideOptions.m
  26. 3
    10
      lib/ios/RNNSplitViewController.h
  27. 20
    19
      lib/ios/RNNSplitViewController.m
  28. 3
    1
      lib/ios/RNNTabBarController.h
  29. 36
    15
      lib/ios/RNNTabBarController.m
  30. 2
    6
      lib/ios/RNNTabBarPresenter.m
  31. 3
    0
      lib/ios/RNNTopTabsViewController.h
  32. 23
    15
      lib/ios/RNNTopTabsViewController.m
  33. 0
    2
      lib/ios/RNNViewControllerPresenter.h
  34. 2
    6
      lib/ios/RNNViewControllerPresenter.m
  35. 8
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  36. 7
    5
      lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m
  37. 46
    7
      lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m
  38. 8
    64
      lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m

+ 1
- 1
e2e/ComplexLayout.test.js View File

@@ -20,7 +20,7 @@ describe('complex layout', () => {
20 20
     await expect(elementByLabel('External component in deep stack')).toBeVisible();
21 21
   });
22 22
 
23
-  test(':android: merge options correctly in SideMenu inside BottomTabs layout', async () => {
23
+  test('merge options correctly in SideMenu inside BottomTabs layout', async () => {
24 24
     await elementById(testIDs.COMPLEX_LAYOUT_BUTTON).tap();
25 25
     await elementById(testIDs.SIDE_MENU_LAYOUT_INSIDE_BOTTOM_TAB).tap();
26 26
     await elementById(testIDs.SECOND_TAB_BAR_BUTTON).tap();

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

@@ -76,6 +76,12 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
76 76
 			self.originalTopBarImages = nil;
77 77
 		}
78 78
 	}
79
+
80
+	if (self.clipToBounds) {
81
+		navigationController.navigationBar.clipsToBounds = [self.clipToBounds boolValue];
82
+	} else {
83
+		navigationController.navigationBar.clipsToBounds = NO;
84
+	}
79 85
 }
80 86
 
81 87
 - (void)storeOriginalTopBarImages:(UINavigationController *)navigationController {

+ 2
- 18
lib/ios/RNNBasePresenter.h View File

@@ -1,25 +1,9 @@
1 1
 #import <Foundation/Foundation.h>
2 2
 #import "RNNNavigationOptions.h"
3 3
 
4
-@protocol RNNPresenterDelegate <NSObject>
5
-
6
-- (void)optionsUpdated;
7
-
8
-@end
9
-
10 4
 @interface RNNBasePresenter : NSObject
11 5
 
12
-@property (nonatomic, weak) id<RNNPresenterDelegate> delegate;
13
-@property (nonatomic, strong) RNNNavigationOptions* options;
14
-
15
-- (instancetype)initWithOptions:(RNNNavigationOptions *)options;
16
-
17
-- (RNNNavigationOptions *)presentWithChildOptions:(RNNNavigationOptions *)childOptions on:(UIViewController *)viewController;
18
-
19
-- (void)present:(RNNNavigationOptions *)options on:(UIViewController *)viewController;
20
-
21
-- (void)presentOn:(UIViewController *)viewController;
22
-
23
-- (void)overrideOptions:(RNNNavigationOptions *)options;
6
+- (void)present:(RNNNavigationOptions *)options onViewControllerDidLoad:(UIViewController *)viewController;
7
+- (void)present:(RNNNavigationOptions *)options onViewControllerWillAppear:(UIViewController *)viewController;
24 8
 
25 9
 @end

+ 3
- 22
lib/ios/RNNBasePresenter.m View File

@@ -2,31 +2,12 @@
2 2
 
3 3
 @implementation RNNBasePresenter
4 4
 
5
-- (instancetype)initWithOptions:(RNNNavigationOptions *)options {
6
-	self = [super init];
7
-	if (self) {
8
-		self.options = options;
9
-	}
10
-	return self;
11
-}
12
-
13
-- (void)presentOn:(UIViewController *)viewController {
14
-	[self.options applyOn:viewController];
15
-}
16
-
17
-- (void)present:(RNNNavigationOptions *)options on:(UIViewController *)viewController {
5
+- (void)present:(RNNNavigationOptions *)options onViewControllerWillAppear:(UIViewController *)viewController {
18 6
 	[options applyOn:viewController];
19 7
 }
20 8
 
21
-- (RNNNavigationOptions *)presentWithChildOptions:(RNNNavigationOptions *)childOptions on:(UIViewController *)viewController {
22
-	RNNNavigationOptions* options = [self.options combineWithOptions:childOptions];
23
-	[self present:options on:viewController];
24
-	
25
-	return options;
26
-}
27
-
28
-- (void)overrideOptions:(RNNNavigationOptions *)options {
29
-	[_options mergeOptions:options overrideOptions:YES];
9
+- (void)present:(RNNNavigationOptions *)options onViewControllerDidLoad:(UIViewController *)viewController {
10
+	[options applyOn:viewController];
30 11
 }
31 12
 
32 13
 @end

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

@@ -73,9 +73,13 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
73 73
 	[CATransaction begin];
74 74
 	[CATransaction setCompletionBlock:completion];
75 75
 	
76
-	[vc.presenter overrideOptions:options];
77
-	[vc.presenter presentOn:vc];
78
-	
76
+	while (vc != nil) {
77
+		if ([vc conformsToProtocol:@protocol(RNNLayoutProtocol)]) {
78
+			[vc mergeOptions:options];
79
+		}
80
+		vc = (UIViewController<RNNLayoutProtocol>*)vc.parentViewController;
81
+	}
82
+
79 83
 	[CATransaction commit];
80 84
 }
81 85
 
@@ -90,7 +94,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
90 94
 	RNNRootViewController *newVc = (RNNRootViewController *)[_controllerFactory createLayoutAndSaveToStore:layout];
91 95
 	UIViewController *fromVC = [_store findComponentForId:componentId];
92 96
 	
93
-	if ([newVc.presenter.options.preview.reactTag floatValue] > 0) {
97
+	if ([newVc.options.preview.reactTag floatValue] > 0) {
94 98
 		UIViewController* vc = [_store findComponentForId:componentId];
95 99
 		
96 100
 		if([vc isKindOfClass:[RNNRootViewController class]]) {
@@ -100,7 +104,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
100 104
       rootVc.previewCallback = ^(UIViewController *vcc) {
101 105
 				RNNRootViewController* rvc  = (RNNRootViewController*)vcc;
102 106
 				[self->_eventEmitter sendOnPreviewCompleted:componentId previewComponentId:newVc.layoutInfo.componentId];
103
-				if ([newVc.presenter.options.preview.commit floatValue] > 0) {
107
+				if ([newVc.options.preview.commit floatValue] > 0) {
104 108
 					[CATransaction begin];
105 109
 					[CATransaction setCompletionBlock:^{
106 110
 						[self->_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}];
@@ -113,27 +117,27 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
113 117
 			
114 118
 			CGSize size = CGSizeMake(rootVc.view.frame.size.width, rootVc.view.frame.size.height);
115 119
 			
116
-			if (newVc.presenter.options.preview.width) {
117
-				size.width = [newVc.presenter.options.preview.width floatValue];
120
+			if (newVc.options.preview.width) {
121
+				size.width = [newVc.options.preview.width floatValue];
118 122
 			}
119 123
 			
120
-			if (newVc.presenter.options.preview.height) {
121
-				size.height = [newVc.presenter.options.preview.height floatValue];
124
+			if (newVc.options.preview.height) {
125
+				size.height = [newVc.options.preview.height floatValue];
122 126
 			}
123 127
 			
124
-			if (newVc.presenter.options.preview.width || newVc.presenter.options.preview.height) {
128
+			if (newVc.options.preview.width || newVc.options.preview.height) {
125 129
 				newVc.preferredContentSize = size;
126 130
 			}
127 131
       
128 132
 			RCTExecuteOnMainQueue(^{
129
-				UIView *view = [[ReactNativeNavigation getBridge].uiManager viewForReactTag:newVc.presenter.options.preview.reactTag];
133
+				UIView *view = [[ReactNativeNavigation getBridge].uiManager viewForReactTag:newVc.options.preview.reactTag];
130 134
 				[rootVc registerForPreviewingWithDelegate:(id)rootVc sourceView:view];
131 135
 			});
132 136
 		}
133 137
 	} else {
134
-		id animationDelegate = (newVc.presenter.options.animations.push.hasCustomAnimation || newVc.isCustomTransitioned) ? newVc : nil;
135
-		[newVc waitForReactViewRender:(newVc.presenter.options.animations.push.waitForRender || animationDelegate) perform:^{
136
-			[_stackManager push:newVc onTop:fromVC animated:newVc.presenter.options.animations.push.enable animationDelegate:animationDelegate completion:^{
138
+		id animationDelegate = (newVc.options.animations.push.hasCustomAnimation || newVc.isCustomTransitioned) ? newVc : nil;
139
+		[newVc waitForReactViewRender:(newVc.options.animations.push.waitForRender || animationDelegate) perform:^{
140
+			[_stackManager push:newVc onTop:fromVC animated:newVc.options.animations.push.enable animationDelegate:animationDelegate completion:^{
137 141
 				[_eventEmitter sendOnNavigationCommandCompletion:push params:@{@"componentId": componentId}];
138 142
 				completion();
139 143
 			} rejection:rejection];
@@ -145,7 +149,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
145 149
 	[self assertReady];
146 150
 	
147 151
 	UIViewController<RNNParentProtocol> *newVC = [_controllerFactory createLayoutAndSaveToStore:layout];
148
-	RNNNavigationOptions* options = [newVC getLeafViewController].presenter.options;
152
+	RNNNavigationOptions* options = [newVC getLeafViewController].options;
149 153
 	UIViewController *fromVC = [_store findComponentForId:componentId];
150 154
 	__weak typeof(RNNEventEmitter*) weakEventEmitter = _eventEmitter;
151 155
 	[_stackManager setStackRoot:newVC fromViewController:fromVC animated:options.animations.setStackRoot.enable completion:^{
@@ -158,12 +162,12 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
158 162
 	[self assertReady];
159 163
 	
160 164
 	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
161
-	[vc.presenter.options mergeWith:options];
165
+	[vc.options mergeWith:options];
162 166
 	
163 167
 	UINavigationController *nvc = vc.navigationController;
164 168
 	
165 169
 	if ([nvc topViewController] == vc) {
166
-		if (vc.presenter.options.animations.pop) {
170
+		if (vc.options.animations.pop) {
167 171
 			nvc.delegate = vc;
168 172
 		} else {
169 173
 			nvc.delegate = nil;
@@ -171,10 +175,10 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
171 175
 	} else {
172 176
 		NSMutableArray * vcs = nvc.viewControllers.mutableCopy;
173 177
 		[vcs removeObject:vc];
174
-		[nvc setViewControllers:vcs animated:vc.presenter.options.animations.pop.enable];
178
+		[nvc setViewControllers:vcs animated:vc.options.animations.pop.enable];
175 179
 	}
176 180
 	
177
-	[_stackManager pop:vc animated:vc.presenter.options.animations.pop.enable completion:^{
181
+	[_stackManager pop:vc animated:vc.options.animations.pop.enable completion:^{
178 182
 		[_store removeComponent:componentId];
179 183
 		[_eventEmitter sendOnNavigationCommandCompletion:pop params:@{@"componentId": componentId}];
180 184
 		completion();
@@ -186,9 +190,9 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
186 190
 - (void)popTo:(NSString*)componentId mergeOptions:(NSDictionary *)options completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
187 191
 	[self assertReady];
188 192
 	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
189
-	[vc.presenter.options mergeWith:options];
193
+	[vc.options mergeWith:options];
190 194
 	
191
-	[_stackManager popTo:vc animated:vc.presenter.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
195
+	[_stackManager popTo:vc animated:vc.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
192 196
 		[_eventEmitter sendOnNavigationCommandCompletion:popTo params:@{@"componentId": componentId}];
193 197
 		[self removePopedViewControllers:poppedViewControllers];
194 198
 		completion();
@@ -198,7 +202,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
198 202
 - (void)popToRoot:(NSString*)componentId mergeOptions:(NSDictionary *)options completion:(RNNTransitionCompletionBlock)completion rejection:(RCTPromiseRejectBlock)rejection {
199 203
 	[self assertReady];
200 204
 	RNNRootViewController *vc = (RNNRootViewController*)[_store findComponentForId:componentId];
201
-	[vc.presenter.options mergeWith:options];
205
+	[vc.options mergeWith:options];
202 206
 	
203 207
 	[CATransaction begin];
204 208
 	[CATransaction setCompletionBlock:^{
@@ -206,7 +210,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
206 210
 		completion();
207 211
 	}];
208 212
 	
209
-	[_stackManager popToRoot:vc animated:vc.presenter.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
213
+	[_stackManager popToRoot:vc animated:vc.options.animations.pop.enable completion:^(NSArray *poppedViewControllers) {
210 214
 		[self removePopedViewControllers:poppedViewControllers];
211 215
 	} rejection:^(NSString *code, NSString *message, NSError *error) {
212 216
 		
@@ -220,8 +224,8 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
220 224
 	
221 225
 	UIViewController<RNNParentProtocol> *newVc = [_controllerFactory createLayoutAndSaveToStore:layout];
222 226
 	
223
-	[newVc.getLeafViewController waitForReactViewRender:newVc.getLeafViewController.presenter.options.animations.showModal.waitForRender perform:^{
224
-		[_modalManager showModal:newVc animated:newVc.getLeafViewController.presenter.options.animations.showModal.enable hasCustomAnimation:newVc.getLeafViewController.presenter.options.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) {
227
+	[newVc.getLeafViewController waitForReactViewRender:newVc.getLeafViewController.options.animations.showModal.waitForRender perform:^{
228
+		[_modalManager showModal:newVc animated:newVc.getLeafViewController.options.animations.showModal.enable hasCustomAnimation:newVc.getLeafViewController.options.animations.showModal.hasCustomAnimation completion:^(NSString *componentId) {
225 229
 			[_eventEmitter sendOnNavigationCommandCompletion:showModal params:@{@"layout": layout}];
226 230
 			completion(componentId);
227 231
 		}];
@@ -236,7 +240,7 @@ static NSString* const setDefaultOptions	= @"setDefaultOptions";
236 240
 		[_eventEmitter sendOnNavigationCommandCompletion:dismissModal params:@{@"componentId": componentId}];
237 241
 	}];
238 242
 	UIViewController<RNNParentProtocol> *modalToDismiss = (UIViewController<RNNParentProtocol>*)[_store findComponentForId:componentId];
239
-	[modalToDismiss.getLeafViewController.presenter.options mergeWith:options];
243
+	[modalToDismiss.getLeafViewController.options mergeWith:options];
240 244
 	
241 245
 	[self removePopedViewControllers:modalToDismiss.navigationController.viewControllers];
242 246
 	

+ 55
- 71
lib/ios/RNNControllerFactory.m View File

@@ -100,134 +100,118 @@
100 100
 
101 101
 - (UIViewController<RNNParentProtocol> *)createComponent:(RNNLayoutNode*)node {
102 102
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
103
-	
104 103
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
105 104
 	
106
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithOptions:options];
107
-	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter isExternalComponent:NO presenter:presenter];
108
-
109
-	if (!component.isCustomViewController) {
105
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
106
+	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter presenter:presenter options:options];
107
+	
108
+	if (!component.isExternalViewController) {
110 109
 		CGSize availableSize = UIApplication.sharedApplication.delegate.window.bounds.size;
111 110
 		[_bridge.uiManager setAvailableSize:availableSize forRootView:component.view];
112 111
 	}
112
+	
113 113
 	return (UIViewController<RNNParentProtocol> *)component;
114 114
 }
115 115
 
116 116
 - (UIViewController<RNNParentProtocol> *)createExternalComponent:(RNNLayoutNode*)node {
117 117
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
118 118
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
119
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
119 120
 
120 121
 	UIViewController* externalVC = [_store getExternalComponent:layoutInfo bridge:_bridge];
121 122
 	
122
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithOptions:options];
123
-	RNNRootViewController* component = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:_creator eventEmitter:_eventEmitter isExternalComponent:YES presenter:presenter];
124
-	
125
-	[component addChildViewController:externalVC];
126
-	[component.view addSubview:externalVC.view];
127
-	[externalVC didMoveToParentViewController:component];
123
+	RNNRootViewController* component = [[RNNRootViewController alloc] initExternalComponentWithLayoutInfo:layoutInfo eventEmitter:_eventEmitter presenter:presenter options:options];
124
+	[component bindViewController:externalVC];
128 125
 	
129 126
 	return (UIViewController<RNNParentProtocol> *)component;
130 127
 }
131 128
 
132 129
 
133 130
 - (UIViewController<RNNParentProtocol> *)createStack:(RNNLayoutNode*)node {
134
-	RNNNavigationController* vc = [[RNNNavigationController alloc] init];
131
+	RNNNavigationControllerPresenter* presenter = [[RNNNavigationControllerPresenter alloc] init];
132
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
135 133
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
136
-
137
-	vc.presenter = [[RNNNavigationControllerPresenter alloc] initWithOptions:options];
138
-	vc.layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
134
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
139 135
 	
140
-	NSMutableArray* controllers = [NSMutableArray new];
141
-	for (NSDictionary* child in node.children) {
142
-		[controllers addObject:[self fromTree:child]];
143
-	}
136
+	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
144 137
 	
145
-	[vc setViewControllers:controllers];
138
+	RNNNavigationController* stack = [[RNNNavigationController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
146 139
 	
147
-	return vc;
140
+	return stack;
148 141
 }
149 142
 
150 143
 -(UIViewController<RNNParentProtocol> *)createTabs:(RNNLayoutNode*)node {
151
-	RNNTabBarController* vc = [[RNNTabBarController alloc] initWithEventEmitter:_eventEmitter];
152
-	vc.layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
144
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
153 145
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
154
-	vc.presenter = [[RNNTabBarPresenter alloc] initWithOptions:options];
146
+	RNNTabBarPresenter* presenter = [[RNNTabBarPresenter alloc] init];
147
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
148
+
149
+	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
155 150
 	
156
-	NSMutableArray* controllers = [NSMutableArray new];
157
-	for (NSDictionary *child in node.children) {
158
-		UIViewController<RNNParentProtocol>* childVc = [self fromTree:child];
159
-		[controllers addObject:childVc];
160
-	}
161
-	[vc setViewControllers:controllers];
151
+	RNNTabBarController* tabsController = [[RNNTabBarController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter eventEmitter:_eventEmitter];
162 152
 	
163
-	return vc;
153
+	return tabsController;
164 154
 }
165 155
 
166 156
 - (UIViewController<RNNParentProtocol> *)createTopTabs:(RNNLayoutNode*)node {
167
-	RNNTopTabsViewController* vc = [[RNNTopTabsViewController alloc] init];
168
-	vc.layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
169
-	
170
-	NSMutableArray* controllers = [NSMutableArray new];
171
-	for (NSDictionary *child in node.children) {
172
-		RNNRootViewController* childVc = (RNNRootViewController*)[self fromTree:child];
173
-		[controllers addObject:childVc];
174
-		[_bridge.uiManager setAvailableSize:vc.contentView.bounds.size forRootView:childVc.view];
175
-	}
157
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
158
+	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
159
+	RNNBasePresenter* presenter = [[RNNBasePresenter alloc] init];
160
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
161
+
162
+	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
176 163
 	
177
-	[vc setViewControllers:controllers];
164
+	RNNTopTabsViewController* topTabsController = [[RNNTopTabsViewController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
178 165
 	
179
-	return vc;
166
+	return topTabsController;
180 167
 }
181 168
 
182 169
 - (UIViewController<RNNParentProtocol> *)createSideMenu:(RNNLayoutNode*)node {
183 170
 	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
184 171
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
185
-	RNNBasePresenter* presenter = [[RNNBasePresenter alloc] initWithOptions:options];
186
-	
187
-	NSMutableArray* childrenVCs = [NSMutableArray new];
172
+	RNNBasePresenter* presenter = [[RNNBasePresenter alloc] init];
173
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
174
+
175
+	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
188 176
 	
189
-	for (NSDictionary *child in node.children) {
190
-		UIViewController *vc = [self fromTree:child];
191
-		[childrenVCs addObject:vc];
192
-	}
193
-	RNNSideMenuController *sideMenu = [[RNNSideMenuController alloc] initWithControllers:childrenVCs presenter:presenter];
194
-	sideMenu.layoutInfo = layoutInfo;
177
+	RNNSideMenuController *sideMenu = [[RNNSideMenuController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
195 178
 	
196 179
 	return sideMenu;
197 180
 }
198 181
 
199 182
 
200 183
 - (UIViewController<RNNParentProtocol> *)createSideMenuChild:(RNNLayoutNode*)node type:(RNNSideMenuChildType)type {
201
-	UIViewController<RNNParentProtocol>* child = [self fromTree:node.children[0]];
184
+	UIViewController<RNNParentProtocol>* childVc = [self fromTree:node.children[0]];
185
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
202 186
 	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
203
-	RNNSideMenuChildVC *sideMenuChild = [[RNNSideMenuChildVC alloc] initWithChild: child type:type];
204
-	sideMenuChild.presenter = [[RNNBasePresenter alloc] initWithOptions:options];
187
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
188
+
189
+	RNNSideMenuChildVC *sideMenuChild = [[RNNSideMenuChildVC alloc] initWithLayoutInfo:layoutInfo childViewControllers:@[childVc] options:options optionsResolver:optionsResolver presenter:[[RNNBasePresenter alloc] init] type:type];
205 190
 	
206 191
 	return sideMenuChild;
207 192
 }
208 193
 
209 194
 - (UIViewController<RNNParentProtocol> *)createSplitView:(RNNLayoutNode*)node {
195
+	RNNLayoutInfo* layoutInfo = [[RNNLayoutInfo alloc] initWithNode:node];
196
+	RNNNavigationOptions* options = [_optionsManager createOptions:node.data[@"options"]];
197
+	RNNBasePresenter* presenter = [[RNNBasePresenter alloc] init];
198
+	RNNParentOptionsResolver* optionsResolver = [[RNNParentOptionsResolver alloc] init];
210 199
 
211
-	NSString* componentId = node.nodeId;
212
-	
213
-	RNNSplitViewOptions* options = [[RNNSplitViewOptions alloc] initWithDict:_optionsManager.defaultOptionsDict];
214
-	[options mergeWith:node.data[@"options"]];
215
-
216
-	RNNSplitViewController* svc = [[RNNSplitViewController alloc] initWithOptions:options withComponentId:componentId rootViewCreator:_creator eventEmitter:_eventEmitter];
217
-
218
-	// We need two children of the node for successful Master / Detail
219
-	NSDictionary *master = node.children[0];
220
-	NSDictionary *detail = node.children[1];
200
+	NSArray *childViewControllers = [self extractChildrenViewControllersFromNode:node];
221 201
 
222
-	// Create view controllers
223
-	RNNRootViewController* masterVc = (RNNRootViewController*)[self fromTree:master];
224
-	RNNRootViewController* detailVc = (RNNRootViewController*)[self fromTree:detail];
202
+	RNNSplitViewController* splitViewController = [[RNNSplitViewController alloc] initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
225 203
 
226
-	// Set the controllers and delegate to masterVC
227
-	svc.viewControllers = [NSArray arrayWithObjects:masterVc, detailVc, nil];
228
-	svc.delegate = masterVc;
204
+	return splitViewController;
205
+}
229 206
 
230
-	return svc;
207
+- (NSArray<UIViewController *> *)extractChildrenViewControllersFromNode:(RNNLayoutNode *)node {
208
+	NSMutableArray* childrenArray = [NSMutableArray new];
209
+	for (NSDictionary* child in node.children) {
210
+		UIViewController* childVc = [self fromTree:child];
211
+		[childrenArray addObject:childVc];
212
+	}
213
+	
214
+	return childrenArray;
231 215
 }
232 216
 
233 217
 @end

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

@@ -7,5 +7,8 @@
7 7
 
8 8
 @property (nonatomic, retain) RNNBasePresenter* presenter;
9 9
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
10
+@property (nonatomic, strong) RNNNavigationOptions* options;
11
+
12
+- (void)mergeOptions:(RNNNavigationOptions *)options;
10 13
 
11 14
 @end

+ 3
- 1
lib/ios/RNNLeafProtocol.h View File

@@ -4,8 +4,10 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
4 4
 
5 5
 @protocol RNNLeafProtocol <RNNLayoutProtocol>
6 6
 
7
+- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock;
8
+
7 9
 - (UIViewController<RNNLeafProtocol> *)getLeafViewController;
8 10
 
9
-- (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock;
11
+- (void)bindViewController:(UIViewController *)viewController;
10 12
 
11 13
 @end

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

@@ -60,7 +60,7 @@
60 60
 
61 61
 -(void)removePendingNextModalIfOnTop:(RNNTransitionCompletionBlock)completion {
62 62
 	UIViewController<RNNParentProtocol> *modalToDismiss = [_pendingModalIdsToDismiss lastObject];
63
-	RNNNavigationOptions* options = modalToDismiss.getLeafViewController.presenter.options;
63
+	RNNNavigationOptions* options = modalToDismiss.getLeafViewController.options;
64 64
 
65 65
 	if(!modalToDismiss) {
66 66
 		return;
@@ -88,7 +88,7 @@
88 88
 	} else {
89 89
 		[modalToDismiss.view removeFromSuperview];
90 90
 		modalToDismiss.view = nil;
91
-		modalToDismiss.getLeafViewController.presenter.options.animations.dismissModal.enable = NO;
91
+		modalToDismiss.getLeafViewController.options.animations.dismissModal.enable = NO;
92 92
 		[self dismissedModal:modalToDismiss];
93 93
 		
94 94
 		if (completion) {

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

@@ -1,10 +1,13 @@
1 1
 #import <UIKit/UIKit.h>
2 2
 #import "RNNParentProtocol.h"
3 3
 #import "RNNNavigationControllerPresenter.h"
4
+#import "RNNParentOptionsResolver.h"
4 5
 
5 6
 @interface RNNNavigationController : UINavigationController <RNNParentProtocol>
6 7
 
7 8
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
8 9
 @property (nonatomic, retain) RNNNavigationControllerPresenter* presenter;
10
+@property (nonatomic, strong) RNNNavigationOptions* options;
11
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
9 12
 
10 13
 @end

+ 25
- 17
lib/ios/RNNNavigationController.m View File

@@ -5,6 +5,19 @@
5 5
 
6 6
 @implementation RNNNavigationController
7 7
 
8
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNNavigationControllerPresenter *)presenter {
9
+	self = [super init];
10
+
11
+	self.presenter = presenter;
12
+	self.options = options;
13
+	self.optionsResolver = optionsResolver;
14
+	self.layoutInfo = layoutInfo;
15
+	
16
+	[self setViewControllers:childViewControllers];
17
+	
18
+	return self;
19
+}
20
+
8 21
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
9 22
 	return self.viewControllers.lastObject.supportedInterfaceOrientations;
10 23
 }
@@ -26,7 +39,7 @@
26 39
 		UIViewController *controller = self.viewControllers[self.viewControllers.count - 2];
27 40
 		if ([controller isKindOfClass:[RNNRootViewController class]]) {
28 41
 			RNNRootViewController *rnnController = (RNNRootViewController *)controller;
29
-			[rnnController.presenter presentOn:rnnController];
42
+			[rnnController.presenter present:rnnController.options onViewControllerDidLoad:rnnController];
30 43
 		}
31 44
 	}
32 45
 	
@@ -34,11 +47,11 @@
34 47
 }
35 48
 
36 49
 - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
37
-	return [[RNNModalAnimation alloc] initWithScreenTransition:self.getLeafViewController.presenter.options.animations.showModal isDismiss:NO];
50
+	return [[RNNModalAnimation alloc] initWithScreenTransition:self.getLeafViewController.options.animations.showModal isDismiss:NO];
38 51
 }
39 52
 
40 53
 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
41
-	return [[RNNModalAnimation alloc] initWithScreenTransition:self.getLeafViewController.presenter.options.animations.dismissModal isDismiss:YES];
54
+	return [[RNNModalAnimation alloc] initWithScreenTransition:self.getLeafViewController.options.animations.dismissModal isDismiss:YES];
42 55
 }
43 56
 
44 57
 - (UIViewController *)getLeafViewController {
@@ -49,24 +62,19 @@
49 62
 	return self.topViewController;
50 63
 }
51 64
 
52
-- (void)willMoveToParentViewController:(UIViewController *)parent {
53
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
54
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:_presenter.options];
55
-	}
65
+- (void)viewDidLoad {
66
+	[super viewDidLoad];
67
+	[_presenter present:self.options onViewControllerDidLoad:self];
56 68
 }
57 69
 
58
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
59
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
60
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
61
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
62
-	}
70
+- (void)willMoveToParentViewController:(UIViewController *)parent {
71
+	[_optionsResolver resolve:self with:self.childViewControllers];
72
+	[_presenter present:self.options onViewControllerDidLoad:self];
63 73
 }
64 74
 
65
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
66
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
67
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
68
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
69
-	}
75
+- (void)mergeOptions:(RNNNavigationOptions *)options {
76
+	[self.presenter present:options onViewControllerWillAppear:self];
70 77
 }
71 78
 
79
+
72 80
 @end

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

@@ -2,8 +2,9 @@
2 2
 
3 3
 @implementation RNNNavigationControllerPresenter
4 4
 
5
-- (void)present:(RNNOptions *)options on:(UINavigationController *)navigationController {
5
+- (void)present:(RNNNavigationOptions *)options onViewControllerDidLoad:(UINavigationController *)navigationController {
6 6
 	[options applyOnNavigationController:navigationController];
7
+
7 8
 }
8 9
 
9 10
 @end

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

@@ -38,6 +38,4 @@ extern const NSInteger TOP_BAR_TRANSPARENT_TAG;
38 38
 @property (nonatomic, strong) NSString* modalPresentationStyle;
39 39
 @property (nonatomic, strong) NSString* modalTransitionStyle;
40 40
 
41
-- (RNNNavigationOptions *)combineWithOptions:(RNNOptions *)options;
42
-
43 41
 @end

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

@@ -10,14 +10,6 @@
10 10
 
11 11
 @implementation RNNNavigationOptions
12 12
 
13
-- (RNNNavigationOptions *)combineWithOptions:(RNNOptions *)options {
14
-	RNNNavigationOptions *navigationOptions = [[RNNNavigationOptions alloc] initWithDict:@{}];
15
-	[navigationOptions mergeOptions:self overrideOptions:YES];
16
-	[navigationOptions mergeOptions:options overrideOptions:YES];
17
-	
18
-	return navigationOptions;
19
-}
20
-
21 13
 - (void)applyOn:(UIViewController *)viewController {
22 14
 	[self.topBar applyOn:viewController];
23 15
 	[self.bottomTabs applyOn:viewController];

+ 8
- 0
lib/ios/RNNParentOptionsResolver.h View File

@@ -0,0 +1,8 @@
1
+#import <Foundation/Foundation.h>
2
+#import "RNNLayoutProtocol.h"
3
+
4
+@interface RNNParentOptionsResolver : NSObject
5
+
6
+- (void)resolve:(UIViewController<RNNLayoutProtocol> *)parent with:(NSArray<UIViewController<RNNLayoutProtocol> *> *)children;
7
+
8
+@end

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

@@ -0,0 +1,9 @@
1
+#import "RNNParentOptionsResolver.h"
2
+
3
+@implementation RNNParentOptionsResolver
4
+
5
+- (void)resolve:(UIViewController<RNNLayoutProtocol> *)parent with:(NSArray<UIViewController<RNNLayoutProtocol> *> *)children {
6
+	
7
+}
8
+
9
+@end

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

@@ -1,12 +1,15 @@
1 1
 #import "RNNLayoutProtocol.h"
2 2
 #import "RNNLeafProtocol.h"
3
+#import "RNNParentOptionsResolver.h"
3 4
 
4 5
 @protocol RNNParentProtocol <RNNLayoutProtocol, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate, UISplitViewControllerDelegate>
5 6
 
6 7
 @required
7 8
 
9
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
10
+
11
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter;
12
+
8 13
 - (UIViewController<RNNLeafProtocol> *)getLeafViewController;
9
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)options;
10
-- (void)performOnChildLoad:(RNNNavigationOptions *)options;
11 14
 
12 15
 @end

+ 13
- 6
lib/ios/RNNRootViewController.h View File

@@ -12,23 +12,30 @@
12 12
 
13 13
 typedef void (^PreviewCallback)(UIViewController *vc);
14 14
 
15
-@interface RNNRootViewController : UIViewController	<RNNLeafProtocol, UIViewControllerPreviewingDelegate, UISearchResultsUpdating, UISearchBarDelegate, UINavigationControllerDelegate, UISplitViewControllerDelegate, RNNPresenterDelegate>
15
+@interface RNNRootViewController : UIViewController	<RNNLeafProtocol, UIViewControllerPreviewingDelegate, UISearchResultsUpdating, UISearchBarDelegate, UINavigationControllerDelegate, UISplitViewControllerDelegate>
16 16
 
17 17
 @property (nonatomic, strong) RNNEventEmitter *eventEmitter;
18 18
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
19 19
 @property (nonatomic, strong) RNNViewControllerPresenter* presenter;
20
+@property (nonatomic, strong) RNNNavigationOptions* options;
21
+
20 22
 @property (nonatomic) id<RNNRootViewCreator> creator;
21 23
 @property (nonatomic, strong) RNNAnimator* animator;
22 24
 @property (nonatomic, strong) UIViewController* previewController;
23 25
 @property (nonatomic, copy) PreviewCallback previewCallback;
24 26
 
25 27
 - (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
26
-			 rootViewCreator:(id<RNNRootViewCreator>)creator
27
-				eventEmitter:(RNNEventEmitter*)eventEmitter
28
-		 isExternalComponent:(BOOL)isExternalComponent
29
-				   presenter:(RNNViewControllerPresenter *)presenter;
28
+				   rootViewCreator:(id<RNNRootViewCreator>)creator
29
+					  eventEmitter:(RNNEventEmitter*)eventEmitter
30
+						 presenter:(RNNViewControllerPresenter *)presenter
31
+						   options:(RNNNavigationOptions *)options;
32
+
33
+- (instancetype)initExternalComponentWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
34
+									   eventEmitter:(RNNEventEmitter*)eventEmitter
35
+										  presenter:(RNNViewControllerPresenter *)presenter
36
+											options:(RNNNavigationOptions *)options;
30 37
 
31
-- (BOOL)isCustomViewController;
38
+- (BOOL)isExternalViewController;
32 39
 - (BOOL)isCustomTransitioned;
33 40
 
34 41
 -(void)onButtonPress:(RNNUIBarButtonItem *)barButtonItem;

+ 58
- 59
lib/ios/RNNRootViewController.m View File

@@ -13,7 +13,6 @@
13 13
 	BOOL _isBeingPresented;
14 14
 }
15 15
 
16
-@property (nonatomic) BOOL isExternalComponent;
17 16
 @property (nonatomic, copy) RNNReactViewReadyCompletionBlock reactViewReadyBlock;
18 17
 
19 18
 @end
@@ -22,22 +21,17 @@
22 21
 
23 22
 @synthesize previewCallback;
24 23
 
25
-- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
26
-				   rootViewCreator:(id<RNNRootViewCreator>)creator
27
-					  eventEmitter:(RNNEventEmitter*)eventEmitter
28
-			   isExternalComponent:(BOOL)isExternalComponent
29
-						 presenter:(RNNViewControllerPresenter *)presenter {
24
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo rootViewCreator:(id<RNNRootViewCreator>)creator eventEmitter:(RNNEventEmitter *)eventEmitter presenter:(RNNViewControllerPresenter *)presenter options:(RNNNavigationOptions *)options {
30 25
 	self = [super init];
31 26
 	self.eventEmitter = eventEmitter;
32 27
 	self.creator = creator;
33
-	self.isExternalComponent = isExternalComponent;
34 28
 	self.layoutInfo = layoutInfo;
35 29
 	self.presenter = presenter;
36
-	self.presenter.delegate = self;
30
+	self.options = options;
37 31
 	
38
-	self.animator = [[RNNAnimator alloc] initWithTransitionOptions:self.presenter.options.customTransition];
32
+	self.animator = [[RNNAnimator alloc] initWithTransitionOptions:self.options.customTransition];
39 33
 	
40
-	if (!self.isExternalComponent) {
34
+	if (self.creator) {
41 35
 		self.view = [creator createRootView:self.layoutInfo.name rootViewId:self.layoutInfo.componentId];
42 36
 		[[NSNotificationCenter defaultCenter] addObserver:self
43 37
 												 selector:@selector(reactViewReady)
@@ -54,13 +48,22 @@
54 48
 	return self;
55 49
 }
56 50
 
51
+- (instancetype)initExternalComponentWithLayoutInfo:(RNNLayoutInfo *)layoutInfo eventEmitter:(RNNEventEmitter *)eventEmitter presenter:(RNNViewControllerPresenter *)presenter options:(RNNNavigationOptions *)options {
52
+	self = [self initWithLayoutInfo:layoutInfo rootViewCreator:nil eventEmitter:eventEmitter presenter:presenter options:options];
53
+	return self;
54
+}
55
+
56
+- (void)bindViewController:(UIViewController *)viewController {
57
+	[self addChildViewController:viewController];
58
+	[self.view addSubview:viewController.view];
59
+	[viewController didMoveToParentViewController:self];
60
+}
61
+
57 62
 -(void)viewWillAppear:(BOOL)animated{
58 63
 	[super viewWillAppear:animated];
59 64
 	_isBeingPresented = YES;
60
-	[_presenter presentOn:self];
61
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
62
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:_presenter.options];
63
-	}
65
+	[_presenter present:self.options onViewControllerWillAppear:self];
66
+	[self initReactCustomViews];
64 67
 }
65 68
 
66 69
 -(void)viewDidAppear:(BOOL)animated {
@@ -79,10 +82,13 @@
79 82
 }
80 83
 
81 84
 - (void)willMoveToParentViewController:(UIViewController *)parent {
82
-	[_presenter presentOn:self];
83
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
84
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:_presenter.options];
85
-	}
85
+	[_presenter present:self.options onViewControllerDidLoad:self];
86
+}
87
+
88
+- (void)mergeOptions:(RNNNavigationOptions *)options {
89
+	[self.options mergeOptions:options overrideOptions:YES];
90
+	[self.presenter present:self.options onViewControllerWillAppear:self];
91
+	[self initReactCustomViews];
86 92
 }
87 93
 
88 94
 - (void)reactViewReady {
@@ -96,7 +102,7 @@
96 102
 
97 103
 
98 104
 - (void)waitForReactViewRender:(BOOL)wait perform:(RNNReactViewReadyCompletionBlock)readyBlock {
99
-	if (wait && !_isExternalComponent) {
105
+	if (wait && !self.isExternalViewController) {
100 106
 		[self onReactViewReady:readyBlock];
101 107
 	} else {
102 108
 		readyBlock();
@@ -108,7 +114,7 @@
108 114
 }
109 115
 
110 116
 - (void)onReactViewReady:(RNNReactViewReadyCompletionBlock)readyBlock {
111
-	if (self.isCustomViewController) {
117
+	if (self.isExternalViewController) {
112 118
 		readyBlock();
113 119
 	} else {
114 120
 		self.reactViewReadyBlock = readyBlock;
@@ -125,7 +131,7 @@
125 131
 	[self.eventEmitter sendOnSearchBarCancelPressed:self.layoutInfo.componentId];
126 132
 }
127 133
 
128
-- (void)optionsUpdated {
134
+- (void)initReactCustomViews {
129 135
 	[self setCustomNavigationTitleView];
130 136
 	[self setCustomNavigationBarView];
131 137
 	[self setCustomNavigationComponentBackground];
@@ -133,11 +139,11 @@
133 139
 
134 140
 - (void)setCustomNavigationTitleView {
135 141
 	if (!_customTitleView && _isBeingPresented) {
136
-		if (self.presenter.options.topBar.title.component.name) {
137
-			_customTitleView = (RNNReactView*)[_creator createRootViewFromComponentOptions:self.presenter.options.topBar.title.component];
142
+		if (self.options.topBar.title.component.name) {
143
+			_customTitleView = (RNNReactView*)[_creator createRootViewFromComponentOptions:self.options.topBar.title.component];
138 144
 			_customTitleView.backgroundColor = UIColor.clearColor;
139
-			[_customTitleView setAlignment:self.presenter.options.topBar.title.component.alignment];
140
-			BOOL isCenter = [self.presenter.options.topBar.title.component.alignment isEqualToString:@"center"];
145
+			[_customTitleView setAlignment:self.options.topBar.title.component.alignment];
146
+			BOOL isCenter = [self.options.topBar.title.component.alignment isEqualToString:@"center"];
141 147
 			__weak RNNReactView *weakTitleView = _customTitleView;
142 148
 			CGRect frame = self.navigationController.navigationBar.bounds;
143 149
 			[_customTitleView setFrame:frame];
@@ -161,8 +167,8 @@
161 167
 
162 168
 - (void)setCustomNavigationBarView {
163 169
 	if (!_customTopBar) {
164
-		if (self.presenter.options.topBar.component.name) {
165
-			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.presenter.options.topBar.component];
170
+		if (self.options.topBar.component.name) {
171
+			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.options.topBar.component];
166 172
 			
167 173
 			_customTopBar = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
168 174
 			reactView.backgroundColor = UIColor.clearColor;
@@ -181,20 +187,14 @@
181 187
 
182 188
 - (void)setCustomNavigationComponentBackground {
183 189
 	if (!_customTopBarBackground) {
184
-		if (self.presenter.options.topBar.background.component.name) {
185
-			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.presenter.options.topBar.background.component];
190
+		if (self.options.topBar.background.component.name) {
191
+			RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.options.topBar.background.component];
186 192
 			
187 193
 			_customTopBarBackground = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
188 194
 			[self.navigationController.navigationBar insertSubview:_customTopBarBackground atIndex:1];
189 195
 		} else if (self.navigationController.navigationBar.subviews.count && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) {
190 196
 			[[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview];
191 197
 		}
192
-		
193
-		if (self.presenter.options.topBar.background.clipToBounds) {
194
-			self.navigationController.navigationBar.clipsToBounds = YES;
195
-		} else {
196
-			self.navigationController.navigationBar.clipsToBounds = NO;
197
-		}
198 198
 	} if (_customTopBarBackground && _customTopBarBackground.superview == nil) {
199 199
 		if (self.navigationController.navigationBar.subviews.count && [[self.navigationController.navigationBar.subviews objectAtIndex:1] isKindOfClass:[RNNCustomTitleView class]]) {
200 200
 			[[self.navigationController.navigationBar.subviews objectAtIndex:1] removeFromSuperview];
@@ -205,17 +205,17 @@
205 205
 }
206 206
 
207 207
 -(BOOL)isCustomTransitioned {
208
-	return self.presenter.options.customTransition.animations != nil;
208
+	return self.options.customTransition.animations != nil;
209 209
 }
210 210
 
211
-- (BOOL)isCustomViewController {
212
-	return self.isExternalComponent;
211
+- (BOOL)isExternalViewController {
212
+	return !self.creator;
213 213
 }
214 214
 
215 215
 - (BOOL)prefersStatusBarHidden {
216
-	if (self.presenter.options.statusBar.visible) {
217
-		return ![self.presenter.options.statusBar.visible boolValue];
218
-	} else if ([self.presenter.options.statusBar.hideWithTopBar boolValue]) {
216
+	if (self.options.statusBar.visible) {
217
+		return ![self.options.statusBar.visible boolValue];
218
+	} else if ([self.options.statusBar.hideWithTopBar boolValue]) {
219 219
 		return self.navigationController.isNavigationBarHidden;
220 220
 	}
221 221
 	
@@ -223,7 +223,7 @@
223 223
 }
224 224
 
225 225
 - (UIStatusBarStyle)preferredStatusBarStyle {
226
-	if (self.presenter.options.statusBar.style && [self.presenter.options.statusBar.style isEqualToString:@"light"]) {
226
+	if (self.options.statusBar.style && [self.options.statusBar.style isEqualToString:@"light"]) {
227 227
 		return UIStatusBarStyleLightContent;
228 228
 	} else {
229 229
 		return UIStatusBarStyleDefault;
@@ -231,20 +231,20 @@
231 231
 }
232 232
 
233 233
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
234
-	return self.presenter.options.layout.supportedOrientations;
234
+	return self.options.layout.supportedOrientations;
235 235
 }
236 236
 
237 237
 - (BOOL)hidesBottomBarWhenPushed
238 238
 {
239
-	if (self.presenter.options.bottomTabs && self.presenter.options.bottomTabs.visible) {
240
-		return ![self.presenter.options.bottomTabs.visible boolValue];
239
+	if (self.options.bottomTabs && self.options.bottomTabs.visible) {
240
+		return ![self.options.bottomTabs.visible boolValue];
241 241
 	}
242 242
 	return NO;
243 243
 }
244 244
 
245 245
 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
246 246
 	RNNRootViewController* vc =  (RNNRootViewController*)viewController;
247
-	if (![vc.presenter.options.topBar.backButton.transition isEqualToString:@"custom"]){
247
+	if (![vc.options.topBar.backButton.transition isEqualToString:@"custom"]){
248 248
 		navigationController.delegate = nil;
249 249
 	}
250 250
 }
@@ -253,26 +253,25 @@
253 253
 								  animationControllerForOperation:(UINavigationControllerOperation)operation
254 254
 											   fromViewController:(UIViewController*)fromVC
255 255
 												 toViewController:(UIViewController*)toVC {
256
-	{
257
-		if (self.animator) {
258
-			return self.animator;
259
-		} else if (operation == UINavigationControllerOperationPush && self.presenter.options.animations.push.hasCustomAnimation) {
260
-			return [[RNNPushAnimation alloc] initWithScreenTransition:self.presenter.options.animations.push];
261
-		} else if (operation == UINavigationControllerOperationPop && self.presenter.options.animations.pop.hasCustomAnimation) {
262
-			return [[RNNPushAnimation alloc] initWithScreenTransition:self.presenter.options.animations.pop];
263
-		} else {
264
-			return nil;
265
-		}
256
+	if (self.animator) {
257
+		return self.animator;
258
+	} else if (operation == UINavigationControllerOperationPush && self.options.animations.push.hasCustomAnimation) {
259
+		return [[RNNPushAnimation alloc] initWithScreenTransition:self.options.animations.push];
260
+	} else if (operation == UINavigationControllerOperationPop && self.options.animations.pop.hasCustomAnimation) {
261
+		return [[RNNPushAnimation alloc] initWithScreenTransition:self.options.animations.pop];
262
+	} else {
263
+		return nil;
266 264
 	}
265
+	
267 266
 	return nil;
268 267
 }
269 268
 
270 269
 - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
271
-	return [[RNNModalAnimation alloc] initWithScreenTransition:self.presenter.options.animations.showModal isDismiss:NO];
270
+	return [[RNNModalAnimation alloc] initWithScreenTransition:self.options.animations.showModal isDismiss:NO];
272 271
 }
273 272
 
274 273
 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
275
-	return [[RNNModalAnimation alloc] initWithScreenTransition:self.presenter.options.animations.dismissModal isDismiss:YES];
274
+	return [[RNNModalAnimation alloc] initWithScreenTransition:self.options.animations.dismissModal isDismiss:YES];
276 275
 }
277 276
 
278 277
 - (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location{
@@ -307,7 +306,7 @@
307 306
 
308 307
 - (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
309 308
 	NSMutableArray *actions = [[NSMutableArray alloc] init];
310
-	for (NSDictionary *previewAction in self.presenter.options.preview.actions) {
309
+	for (NSDictionary *previewAction in self.options.preview.actions) {
311 310
 		UIPreviewAction *action = [self convertAction:previewAction];
312 311
 		NSDictionary *actionActions = previewAction[@"actions"];
313 312
 		if (actionActions.count > 0) {

+ 0
- 19
lib/ios/RNNSideMenu/MMDrawerController/MMDrawerController.m View File

@@ -21,7 +21,6 @@
21 21
 
22 22
 #import "MMDrawerController.h"
23 23
 #import "UIViewController+MMDrawerController.h"
24
-#import "RNNNavigationOptions.h"
25 24
 
26 25
 #import <QuartzCore/QuartzCore.h>
27 26
 
@@ -480,24 +479,6 @@ static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide";
480 479
     }
481 480
 }
482 481
 
483
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
484
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
485
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:childOptions];
486
-	}
487
-}
488
-
489
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
490
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
491
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:childOptions];
492
-	}
493
-}
494
-
495
-- (void)willMoveToParentViewController:(UIViewController *)parent {
496
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
497
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:nil];
498
-	}
499
-}
500
-
501 482
 -(void)setCenterViewController:(UIViewController *)newCenterViewController withCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{
502 483
     
503 484
     if(self.openSide == MMDrawerSideNone){

+ 3
- 9
lib/ios/RNNSideMenuChildVC.h View File

@@ -1,11 +1,3 @@
1
-//
2
-//  RNNSideMenuChildVC.h
3
-//  ReactNativeNavigation
4
-//
5
-//  Created by Ran Greenberg on 09/02/2017.
6
-//  Copyright © 2017 Wix. All rights reserved.
7
-//
8
-
9 1
 #import <UIKit/UIKit.h>
10 2
 #import "RNNParentProtocol.h"
11 3
 
@@ -23,7 +15,9 @@ typedef NS_ENUM(NSInteger, RNNSideMenuChildType) {
23 15
 
24 16
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
25 17
 @property (nonatomic, retain) RNNBasePresenter* presenter;
18
+@property (nonatomic, strong) RNNNavigationOptions* options;
19
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
26 20
 
27
--(instancetype) initWithChild:(UIViewController<RNNParentProtocol>*)child type:(RNNSideMenuChildType)type;
21
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter type:(RNNSideMenuChildType)type;
28 22
 
29 23
 @end

+ 28
- 30
lib/ios/RNNSideMenuChildVC.m View File

@@ -1,58 +1,56 @@
1
-//
2
-//  RNNSideMenuChildVC.m
3
-//  ReactNativeNavigation
4
-//
5
-//  Created by Ran Greenberg on 09/02/2017.
6
-//  Copyright © 2017 Wix. All rights reserved.
7
-//
8
-
9 1
 #import "RNNSideMenuChildVC.h"
10 2
 
11 3
 @interface RNNSideMenuChildVC ()
12 4
 
13 5
 @property (readwrite) RNNSideMenuChildType type;
14
-@property (readwrite) UIViewController<RNNParentProtocol> *child;
6
+@property (nonatomic, retain) UIViewController<RNNParentProtocol> *child;
15 7
 
16 8
 @end
17 9
 
18 10
 @implementation RNNSideMenuChildVC
19 11
 
20
--(instancetype) initWithChild:(UIViewController<RNNParentProtocol>*)child type:(RNNSideMenuChildType)type {
12
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter type:(RNNSideMenuChildType)type {
13
+	self = [self initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
14
+	
15
+	self.type = type;
16
+
17
+	return self;
18
+}
19
+
20
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter {
21 21
 	self = [super init];
22 22
 	
23
+	self.presenter = presenter;
24
+	self.options = options;
25
+	self.layoutInfo = layoutInfo;
26
+	self.optionsResolver = optionsResolver;
27
+	
28
+	[self bindChildViewControllers:childViewControllers];
29
+	
30
+	return self;
31
+}
32
+
33
+- (void)bindChildViewControllers:(NSArray<UIViewController<RNNParentProtocol> *> *)viewControllers {
34
+	UIViewController<RNNParentProtocol>* child = viewControllers[0];
35
+	
23 36
 	self.child = child;
24 37
 	[self addChildViewController:self.child];
25 38
 	[self.child.view setFrame:self.view.bounds];
26 39
 	[self.view addSubview:self.child.view];
27 40
 	[self.view bringSubviewToFront:self.child.view];
28
-
29
-	self.type = type;
30
-	
31
-	return self;
32 41
 }
33 42
 
34 43
 - (UIViewController *)getLeafViewController {
35 44
 	return [self.child getLeafViewController];
36 45
 }
37 46
 
38
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
39
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
40
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
41
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
42
-	}
43
-}
44
-
45
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
46
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
47
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
48
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
49
-	}
47
+- (void)willMoveToParentViewController:(UIViewController *)parent {
48
+	[_optionsResolver resolve:self with:self.childViewControllers];
49
+	[_presenter present:self.options onViewControllerDidLoad:self];
50 50
 }
51 51
 
52
-- (void)willMoveToParentViewController:(UIViewController *)parent {
53
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
54
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:_presenter.options];
55
-	}
52
+- (void)mergeOptions:(RNNNavigationOptions *)options {
53
+	[self.presenter present:options onViewControllerWillAppear:self];
56 54
 }
57 55
 
58 56
 - (UIStatusBarStyle)preferredStatusBarStyle {

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

@@ -20,8 +20,8 @@
20 20
 
21 21
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
22 22
 @property (nonatomic, retain) RNNBasePresenter* presenter;
23
-
24
--(instancetype)initWithControllers:(NSArray*)controllers presenter:(RNNBasePresenter *)presenter;
23
+@property (nonatomic, strong) RNNNavigationOptions* options;
24
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
25 25
 
26 26
 -(void)showSideMenu:(MMDrawerSide)side animated:(BOOL)animated;
27 27
 -(void)hideSideMenu:(MMDrawerSide)side animated:(BOOL)animated;

+ 18
- 23
lib/ios/RNNSideMenuController.m View File

@@ -21,24 +21,31 @@
21 21
 
22 22
 @implementation RNNSideMenuController
23 23
 
24
--(instancetype)initWithControllers:(NSArray*)controllers presenter:(RNNBasePresenter *)presenter
25
-{
24
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter {
26 25
 	self = [super init];
26
+	
27 27
 	self.presenter = presenter;
28
+	self.options = options;
29
+	self.layoutInfo = layoutInfo;
30
+	self.optionsResolver = optionsResolver;
31
+	
32
+	[self bindChildViewControllers:childViewControllers];
28 33
 	
29
-	[self setControllers:controllers];
34
+	return self;
35
+}
36
+
37
+- (void)bindChildViewControllers:(NSArray<UIViewController<RNNLayoutProtocol> *> *)viewControllers {
38
+	[self setControllers:viewControllers];
30 39
 	
31 40
 	self.sideMenu = [[MMDrawerController alloc] initWithCenterViewController:self.center leftDrawerViewController:self.left rightDrawerViewController:self.right];
32 41
 	
33 42
 	self.sideMenu.openDrawerGestureModeMask = MMOpenDrawerGestureModeAll;
34 43
 	self.sideMenu.closeDrawerGestureModeMask = MMCloseDrawerGestureModeAll;
35
-
44
+	
36 45
 	[self addChildViewController:self.sideMenu];
37 46
 	[self.sideMenu.view setFrame:self.view.bounds];
38 47
 	[self.view addSubview:self.sideMenu.view];
39 48
 	[self.view bringSubviewToFront:self.sideMenu.view];
40
-	
41
-	return self;
42 49
 }
43 50
 
44 51
 -(void)showSideMenu:(MMDrawerSide)side animated:(BOOL)animated {
@@ -94,25 +101,13 @@
94 101
 	return [self.center getLeafViewController];
95 102
 }
96 103
 
97
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
98
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
99
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
100
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
101
-	}
102
-}
103
-
104
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
105
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
106
-		if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
107
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
108
-	}
109
-}
110
-
111 104
 - (void)willMoveToParentViewController:(UIViewController *)parent {
112
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
113
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:_presenter.options];
114
-	}
105
+	[_optionsResolver resolve:self with:self.childViewControllers];
106
+	[_presenter present:self.options onViewControllerDidLoad:self];
115 107
 }
116 108
 
109
+- (void)mergeOptions:(RNNNavigationOptions *)options {
110
+	[self.presenter present:options onViewControllerWillAppear:self];
111
+}
117 112
 
118 113
 @end

+ 15
- 2
lib/ios/RNNSideMenuSideOptions.m View File

@@ -5,8 +5,8 @@
5 5
 @implementation RNNSideMenuSideOptions
6 6
 
7 7
 - (void)applyOnSide:(MMDrawerSide)side viewController:(UIViewController *)viewController {
8
-	RNNSideMenuController* sideMenuController = (RNNSideMenuController*)UIApplication.sharedApplication.delegate.window.rootViewController;
9
-	if (sideMenuController && [sideMenuController isKindOfClass:[RNNSideMenuController class]]) {
8
+	RNNSideMenuController* sideMenuController = [self getParentSideMenuControllerFromChild:viewController];
9
+	if (sideMenuController) {
10 10
 		if (self.enabled) {
11 11
 			switch (side) {
12 12
 				case MMDrawerSideRight:
@@ -62,6 +62,19 @@
62 62
 	[self resetOptions];
63 63
 }
64 64
 
65
+- (RNNSideMenuController *)getParentSideMenuControllerFromChild:(UIViewController *)childViewController {
66
+	UIViewController* vc = childViewController;
67
+	while (vc) {
68
+		if ([vc isKindOfClass:[RNNSideMenuController class]]) {
69
+			return (RNNSideMenuController *)vc;
70
+		}
71
+		
72
+		vc = vc.parentViewController;
73
+	}
74
+	
75
+	return nil;
76
+}
77
+
65 78
 - (void)resetOptions {
66 79
 	self.visible = nil;
67 80
 	self.enabled = nil;

+ 3
- 10
lib/ios/RNNSplitViewController.h View File

@@ -10,18 +10,11 @@
10 10
 #import "RNNTopTabsViewController.h"
11 11
 #import "RNNParentProtocol.h"
12 12
 
13
-@interface RNNSplitViewController : UISplitViewController	<RNNParentProtocol>
13
+@interface RNNSplitViewController : UISplitViewController <RNNParentProtocol>
14 14
 
15
-@property (nonatomic, strong) RNNSplitViewOptions* options;
16
-@property (nonatomic, strong) RNNEventEmitter *eventEmitter;
17
-@property (nonatomic, strong) NSString* componentId;
15
+@property (nonatomic, strong) RNNNavigationOptions* options;
18 16
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
19 17
 @property (nonatomic, retain) RNNBasePresenter* presenter;
20
-@property (nonatomic) id<RNNRootViewCreator> creator;
21
-
22
--(instancetype)initWithOptions:(RNNSplitViewOptions*)options
23
-			withComponentId:(NSString*)componentId
24
-			rootViewCreator:(id<RNNRootViewCreator>)creator
25
-				  eventEmitter:(RNNEventEmitter*)eventEmitter;
18
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
26 19
 
27 20
 @end

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

@@ -2,21 +2,27 @@
2 2
 
3 3
 @implementation RNNSplitViewController
4 4
 
5
--(instancetype)initWithOptions:(RNNSplitViewOptions*)options
6
-			withComponentId:(NSString*)componentId
7
-			rootViewCreator:(id<RNNRootViewCreator>)creator
8
-			   eventEmitter:(RNNEventEmitter*)eventEmitter {
5
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter {
9 6
 	self = [super init];
10
-	self.componentId = componentId;
7
+	
8
+	self.presenter = presenter;
11 9
 	self.options = options;
12
-	self.eventEmitter = eventEmitter;
13
-	self.creator = creator;
14
-
10
+	self.layoutInfo = layoutInfo;
11
+	self.optionsResolver = optionsResolver;
12
+	
15 13
 	self.navigationController.delegate = self;
16
-
14
+	
15
+	[self bindChildViewControllers:childViewControllers];
16
+	
17 17
 	return self;
18 18
 }
19 19
 
20
+- (void)bindChildViewControllers:(NSArray<UIViewController<RNNLayoutProtocol> *> *)viewControllers {
21
+	[self setViewControllers:viewControllers];
22
+	UIViewController<UISplitViewControllerDelegate>* masterViewController = viewControllers[0];
23
+	self.delegate = masterViewController;
24
+}
25
+
20 26
 -(void)viewWillAppear:(BOOL)animated{
21 27
 	[super viewWillAppear:animated];
22 28
 	[self.options applyOn:self];
@@ -26,18 +32,13 @@
26 32
 	return self;
27 33
 }
28 34
 
29
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
30
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
31
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
32
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
33
-	}
35
+- (void)willMoveToParentViewController:(UIViewController *)parent {
36
+	[_optionsResolver resolve:self with:self.viewControllers];
37
+	[_presenter present:self.options onViewControllerDidLoad:self];
34 38
 }
35 39
 
36
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
37
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
38
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
39
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
40
-	}
40
+- (void)mergeOptions:(RNNNavigationOptions *)options {
41
+	[self.presenter present:options onViewControllerWillAppear:self];
41 42
 }
42 43
 
43 44
 @end

+ 3
- 1
lib/ios/RNNTabBarController.h View File

@@ -5,11 +5,13 @@
5 5
 
6 6
 @interface RNNTabBarController : UITabBarController <RNNParentProtocol, UITabBarControllerDelegate>
7 7
 
8
-- (instancetype)initWithEventEmitter:(RNNEventEmitter*)eventEmitter;
8
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter eventEmitter:(RNNEventEmitter *)eventEmitter;
9 9
 
10 10
 - (void)setSelectedIndexByComponentID:(NSString *)componentID;
11 11
 
12 12
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
13 13
 @property (nonatomic, retain) RNNBasePresenter* presenter;
14
+@property (nonatomic, strong) RNNNavigationOptions* options;
15
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
14 16
 
15 17
 @end

+ 36
- 15
lib/ios/RNNTabBarController.m View File

@@ -8,25 +8,41 @@
8 8
 	RNNEventEmitter *_eventEmitter;
9 9
 }
10 10
 
11
-- (instancetype)initWithEventEmitter:(id)eventEmitter {
12
-	self = [super init];
11
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
12
+			  childViewControllers:(NSArray *)childViewControllers
13
+						   options:(RNNNavigationOptions *)options
14
+				   optionsResolver:(RNNParentOptionsResolver *)optionsResolver
15
+						 presenter:(RNNBasePresenter *)presenter
16
+					  eventEmitter:(RNNEventEmitter *)eventEmitter {
17
+	self = [self initWithLayoutInfo:layoutInfo childViewControllers:childViewControllers options:options optionsResolver:optionsResolver presenter:presenter];
18
+	
13 19
 	_eventEmitter = eventEmitter;
14
-	self.delegate = self;
20
+	
15 21
 	return self;
16 22
 }
17 23
 
18
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
19
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
20
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
21
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
22
-	}
24
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo
25
+			  childViewControllers:(NSArray *)childViewControllers
26
+						   options:(RNNNavigationOptions *)options
27
+				   optionsResolver:(RNNParentOptionsResolver *)optionsResolver
28
+						 presenter:(RNNBasePresenter *)presenter {
29
+	self = [super init];
30
+	
31
+	self.presenter = presenter;
32
+	self.options = options;
33
+	self.layoutInfo = layoutInfo;
34
+	self.optionsResolver = optionsResolver;
35
+	
36
+	[self setViewControllers:childViewControllers];
37
+	
38
+	return self;
23 39
 }
24 40
 
25
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
26
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
27
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
28
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
29
-	}
41
+- (instancetype)initWithEventEmitter:(id)eventEmitter {
42
+	self = [super init];
43
+	_eventEmitter = eventEmitter;
44
+	self.delegate = self;
45
+	return self;
30 46
 }
31 47
 
32 48
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
@@ -56,8 +72,13 @@
56 72
 	return ((UIViewController<RNNParentProtocol>*)self.selectedViewController).preferredStatusBarStyle;
57 73
 }
58 74
 
59
-- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers {
60
-	[super setViewControllers:viewControllers];
75
+- (void)willMoveToParentViewController:(UIViewController *)parent {
76
+	[_optionsResolver resolve:self with:self.viewControllers];
77
+	[_presenter present:self.options onViewControllerDidLoad:self];
78
+}
79
+
80
+- (void)mergeOptions:(RNNNavigationOptions *)options {
81
+	[self.presenter present:options onViewControllerWillAppear:self];
61 82
 }
62 83
 
63 84
 #pragma mark UITabBarControllerDelegate

+ 2
- 6
lib/ios/RNNTabBarPresenter.m View File

@@ -2,12 +2,8 @@
2 2
 
3 3
 @implementation RNNTabBarPresenter
4 4
 
5
-- (void)presentOn:(UITabBarController *)tabBarController {
6
-	[self.options applyOnTabBarController:tabBarController];
7
-}
8
-
9
-- (void)present:(RNNNavigationOptions *)options on:(UITabBarController *)tabBarController {
10
-	[self.options applyOnTabBarController:tabBarController];
5
+- (void)present:(RNNNavigationOptions *)options onViewControllerDidLoad:(UITabBarController *)tabBarController {
6
+	[options applyOnTabBarController:tabBarController];
11 7
 }
12 8
 
13 9
 @end

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

@@ -6,8 +6,11 @@
6 6
 @interface RNNTopTabsViewController : UIViewController <RNNParentProtocol>
7 7
 
8 8
 @property (nonatomic, retain) UIView* contentView;
9
+
9 10
 @property (nonatomic, retain) RNNLayoutInfo* layoutInfo;
10 11
 @property (nonatomic, retain) RNNBasePresenter* presenter;
12
+@property (nonatomic, strong) RNNNavigationOptions* options;
13
+@property (nonatomic, strong) RNNParentOptionsResolver* optionsResolver;
11 14
 
12 15
 - (void)setViewControllers:(NSArray*)viewControllers;
13 16
 - (void)viewController:(UIViewController*)vc changedTitle:(NSString*)title;

+ 23
- 15
lib/ios/RNNTopTabsViewController.m View File

@@ -13,6 +13,19 @@
13 13
 
14 14
 @implementation RNNTopTabsViewController
15 15
 
16
+- (instancetype)initWithLayoutInfo:(RNNLayoutInfo *)layoutInfo childViewControllers:(NSArray *)childViewControllers options:(RNNNavigationOptions *)options optionsResolver:(RNNParentOptionsResolver *)optionsResolver presenter:(RNNBasePresenter *)presenter {
17
+	self = [self init];
18
+	
19
+	self.presenter = presenter;
20
+	self.options = options;
21
+	self.layoutInfo = layoutInfo;
22
+	self.optionsResolver = optionsResolver;
23
+	
24
+	[self setViewControllers:childViewControllers];
25
+	
26
+	return self;
27
+}
28
+
16 29
 - (instancetype)init {
17 30
 	self = [super init];
18 31
 	
@@ -57,7 +70,7 @@
57 70
 	_viewControllers = viewControllers;
58 71
 	for (RNNRootViewController* childVc in viewControllers) {
59 72
 		[childVc.view setFrame:_contentView.bounds];
60
-		[childVc.presenter.options.topTab applyOn:childVc];
73
+		[childVc.options.topTab applyOn:childVc];
61 74
 	}
62 75
 	
63 76
 	[self setSelectedViewControllerIndex:0];
@@ -72,24 +85,19 @@
72 85
     [super viewDidLoad];
73 86
 }
74 87
 
75
-#pragma mark RNNParentProtocol
76
-
77
-- (UIViewController *)getLeafViewController {
78
-	return _currentViewController;
88
+- (void)willMoveToParentViewController:(UIViewController *)parent {
89
+	[_optionsResolver resolve:self with:_viewControllers];
90
+	[_presenter present:self.options onViewControllerDidLoad:self];
79 91
 }
80 92
 
81
-- (void)performOnChildLoad:(RNNNavigationOptions *)childOptions {
82
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
83
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildLoad:)]) {
84
-		[self.parentViewController performSelector:@selector(performOnChildLoad:) withObject:combinedOptions];
85
-	}
93
+- (void)mergeOptions:(RNNNavigationOptions *)options {
94
+	[self.presenter present:options onViewControllerWillAppear:self];
86 95
 }
87 96
 
88
-- (void)performOnChildWillAppear:(RNNNavigationOptions *)childOptions {
89
-	RNNNavigationOptions* combinedOptions = [_presenter presentWithChildOptions:childOptions on:self];
90
-	if ([self.parentViewController respondsToSelector:@selector(performOnChildWillAppear:)]) {
91
-		[self.parentViewController performSelector:@selector(performOnChildWillAppear:) withObject:combinedOptions];
92
-	}
97
+#pragma mark RNNParentProtocol
98
+
99
+- (UIViewController *)getLeafViewController {
100
+	return _currentViewController;
93 101
 }
94 102
 
95 103
 @end

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

@@ -3,6 +3,4 @@
3 3
 
4 4
 @interface RNNViewControllerPresenter : RNNBasePresenter
5 5
 
6
-- (void)presentOn:(UIViewController *)viewController;
7
-
8 6
 @end

+ 2
- 6
lib/ios/RNNViewControllerPresenter.m View File

@@ -2,12 +2,8 @@
2 2
 
3 3
 @implementation RNNViewControllerPresenter
4 4
 
5
-- (void)presentOn:(UIViewController *)viewController {
6
-	[self.options applyOn:viewController];
7
-	
8
-	if ([self.delegate respondsToSelector:@selector(optionsUpdated)]) {
9
-		[self.delegate optionsUpdated];
10
-	}
5
+- (void)present:(RNNNavigationOptions *)options onViewControllerDidLoad:(UIViewController *)viewController {
6
+	[options applyOn:viewController];
11 7
 }
12 8
 
13 9
 @end

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

@@ -115,6 +115,8 @@
115 115
 		50644A2120E11A720026709C /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 50644A1F20E11A720026709C /* Constants.m */; };
116 116
 		506A2B1420973DFD00F43A95 /* RNNErrorHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 506A2B1220973DFD00F43A95 /* RNNErrorHandler.h */; };
117 117
 		506A2B1520973DFD00F43A95 /* RNNErrorHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 506A2B1320973DFD00F43A95 /* RNNErrorHandler.m */; };
118
+		506F62E22163912E00AD0D0A /* RNNParentOptionsResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 506F62E02163912E00AD0D0A /* RNNParentOptionsResolver.h */; };
119
+		506F62E32163912E00AD0D0A /* RNNParentOptionsResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 506F62E12163912E00AD0D0A /* RNNParentOptionsResolver.m */; };
118 120
 		50706E6D20CE7CA5003345C3 /* UIImage+tint.h in Headers */ = {isa = PBXBuildFile; fileRef = 50706E6B20CE7CA5003345C3 /* UIImage+tint.h */; };
119 121
 		50706E6E20CE7CA5003345C3 /* UIImage+tint.m in Sources */ = {isa = PBXBuildFile; fileRef = 50706E6C20CE7CA5003345C3 /* UIImage+tint.m */; };
120 122
 		50762D08205E96C200E3D18A /* RNNModalAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50762D06205E96C200E3D18A /* RNNModalAnimation.h */; };
@@ -358,6 +360,8 @@
358 360
 		50644A1F20E11A720026709C /* Constants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = "<group>"; };
359 361
 		506A2B1220973DFD00F43A95 /* RNNErrorHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNErrorHandler.h; sourceTree = "<group>"; };
360 362
 		506A2B1320973DFD00F43A95 /* RNNErrorHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNErrorHandler.m; sourceTree = "<group>"; };
363
+		506F62E02163912E00AD0D0A /* RNNParentOptionsResolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNParentOptionsResolver.h; sourceTree = "<group>"; };
364
+		506F62E12163912E00AD0D0A /* RNNParentOptionsResolver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNParentOptionsResolver.m; sourceTree = "<group>"; };
361 365
 		50706E6B20CE7CA5003345C3 /* UIImage+tint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImage+tint.h"; sourceTree = "<group>"; };
362 366
 		50706E6C20CE7CA5003345C3 /* UIImage+tint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIImage+tint.m"; sourceTree = "<group>"; };
363 367
 		50762D06205E96C200E3D18A /* RNNModalAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNModalAnimation.h; sourceTree = "<group>"; };
@@ -702,6 +706,8 @@
702 706
 				50570BE82063E09B006A1B5C /* RNNTitleViewHelper.h */,
703 707
 				501CD31D214A5B6900A6E225 /* RNNLayoutInfo.h */,
704 708
 				501CD31E214A5B6900A6E225 /* RNNLayoutInfo.m */,
709
+				506F62E02163912E00AD0D0A /* RNNParentOptionsResolver.h */,
710
+				506F62E12163912E00AD0D0A /* RNNParentOptionsResolver.m */,
705 711
 				50220F46212ABDFD004C2B0A /* RNNReactRootView.h */,
706 712
 				50220F47212ABDFD004C2B0A /* RNNReactRootView.m */,
707 713
 				501E0215213E7EA3003365C5 /* RNNReactView.h */,
@@ -912,6 +918,7 @@
912 918
 				50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */,
913 919
 				263905C21E4C6F440023D7D3 /* SidebarAnimation.h in Headers */,
914 920
 				E8E518361F83B94A000467AC /* RNNViewLocation.h in Headers */,
921
+				506F62E22163912E00AD0D0A /* RNNParentOptionsResolver.h in Headers */,
915 922
 				505EDD34214E7B7B0071C7DE /* RNNLeafProtocol.h in Headers */,
916 923
 				263905B51E4C6F440023D7D3 /* MMExampleDrawerVisualStateManager.h in Headers */,
917 924
 				50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */,
@@ -1181,6 +1188,7 @@
1181 1188
 				50F5DFC61F407AA0001A00BC /* RNNNavigationController.m in Sources */,
1182 1189
 				21B85E5D1F44480200B314B5 /* RNNNavigationButtons.m in Sources */,
1183 1190
 				E8E518371F83B94A000467AC /* RNNViewLocation.m in Sources */,
1191
+				506F62E32163912E00AD0D0A /* RNNParentOptionsResolver.m in Sources */,
1184 1192
 				E3458D3E20BD9CE40023149B /* RNNPreviewOptions.m in Sources */,
1185 1193
 				263905C91E4C6F440023D7D3 /* SidebarFlipboardAnimation.m in Sources */,
1186 1194
 			);

+ 7
- 5
lib/ios/ReactNativeNavigationTests/RNNCommandsHandlerTest.m View File

@@ -97,12 +97,14 @@
97 97
 	RNNNavigationOptions* initialOptions = [[RNNNavigationOptions alloc] initWithDict:@{}];
98 98
 	initialOptions.topBar.title.text = @"the title";
99 99
 	RNNLayoutInfo* layoutInfo = [RNNLayoutInfo new];
100
+	RNNTestRootViewCreator* creator = [[RNNTestRootViewCreator alloc] init];
100 101
 	
101
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithOptions:initialOptions];
102
-	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:[[RNNTestRootViewCreator alloc] init] eventEmitter:nil isExternalComponent:NO presenter:presenter];
102
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
103
+	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:creator eventEmitter:nil presenter:presenter options:initialOptions];
103 104
 	
104 105
 	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:vc];
105
-	nav.presenter = [[RNNNavigationControllerPresenter alloc] initWithOptions:initialOptions];
106
+	nav.presenter = [[RNNNavigationControllerPresenter alloc] init];
107
+	nav.options = initialOptions;
106 108
 	
107 109
 	[vc viewWillAppear:false];
108 110
 	XCTAssertTrue([vc.navigationItem.title isEqual:@"the title"]);
@@ -123,8 +125,8 @@
123 125
 	RNNNavigationOptions* initialOptions = [[RNNNavigationOptions alloc] initWithDict:@{}];
124 126
 	initialOptions.topBar.title.text = @"the title";
125 127
 	
126
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithOptions:initialOptions];
127
-	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:[[RNNTestRootViewCreator alloc] init] eventEmitter:nil isExternalComponent:NO presenter:presenter];
128
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
129
+	RNNRootViewController* vc = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:[[RNNTestRootViewCreator alloc] init] eventEmitter:nil presenter:presenter options:initialOptions];
128 130
 	
129 131
 	__unused RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:vc];
130 132
 	[vc viewWillAppear:false];

+ 46
- 7
lib/ios/ReactNativeNavigationTests/RNNNavigationControllerTest.m View File

@@ -12,17 +12,18 @@
12 12
 	RNNRootViewController* _vc1;
13 13
 	RNNRootViewController* _vc2;
14 14
 	UIViewController* _vc3;
15
-	RNNViewControllerPresenter* _presenter;
16 15
 }
17 16
 
18 17
 - (void)setUp {
19 18
     [super setUp];
20 19
 	
21
-	_presenter = [[RNNViewControllerPresenter alloc] initWithOptions:[[RNNNavigationOptions alloc] initWithDict:@{}]];
22
-	self.uut = [[RNNNavigationController alloc] init];
23
-	_vc1 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil isExternalComponent:NO presenter:_presenter];
24
-	_vc2 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil isExternalComponent:NO presenter:_presenter];
20
+	_vc1 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNViewControllerPresenter alloc] init] options:nil];
21
+	_vc2 = [[RNNRootViewController alloc] initWithLayoutInfo:nil rootViewCreator:nil eventEmitter:nil presenter:[[RNNViewControllerPresenter alloc] init] options:nil];
25 22
 	_vc3 = [UIViewController new];
23
+	
24
+	self.uut = [[RNNNavigationController alloc] initWithRootViewController:_vc1];
25
+	self.uut.options = [[RNNNavigationOptions alloc] initWithDict:@{}];
26
+	self.uut.presenter = [[RNNNavigationControllerPresenter alloc] init];;
26 27
 }
27 28
 
28 29
 - (void)testChildViewControllerForStatusBarStyle_shouldReturnTopViewController {
@@ -34,10 +35,48 @@
34 35
 }
35 36
 
36 37
 - (void)testPreferredStatusBarStyle_shouldReturnLeafPreferredStatusBarStyle {
37
-	[self.uut setViewControllers:@[_vc1]];
38
-	self.uut.getLeafViewController.presenter.options.statusBar.style = @"light";
38
+	self.uut.getLeafViewController.options.statusBar.style = @"light";
39 39
 	XCTAssertTrue(self.uut.preferredStatusBarStyle == self.uut.getLeafViewController.preferredStatusBarStyle);
40 40
 }
41 41
 
42
+- (void)testPopGestureEnabled_false {
43
+	NSNumber* popGestureEnabled = @(0);
44
+	self.uut.options.popGesture = popGestureEnabled;
45
+	
46
+	[self.uut viewDidLoad];
47
+
48
+	XCTAssertFalse(self.uut.interactivePopGestureRecognizer.enabled);
49
+}
50
+
51
+- (void)testPopGestureEnabled_true {
52
+	NSNumber* popGestureEnabled = @(1);
53
+	self.uut.options.popGesture = popGestureEnabled;
54
+	
55
+	[self.uut viewDidLoad];
56
+	
57
+	XCTAssertTrue(self.uut.interactivePopGestureRecognizer.enabled);
58
+}
59
+
60
+- (void)testRootBackgroundImage {
61
+	UIImage* rootBackgroundImage = [[UIImage alloc] init];
62
+	self.uut.options.rootBackgroundImage = rootBackgroundImage;
63
+	[self.uut viewDidLoad];
64
+	XCTAssertTrue([[(UIImageView*)self.uut.view.subviews[0] image] isEqual:rootBackgroundImage]);
65
+}
66
+
67
+- (void)testTopBarBackgroundClipToBounds_true {
68
+	self.uut.options.topBar.background.clipToBounds = @(1);
69
+	[self.uut viewDidLoad];
70
+
71
+	XCTAssertTrue(self.uut.navigationBar.clipsToBounds);
72
+}
73
+
74
+- (void)testTopBarBackgroundClipToBounds_false {
75
+	self.uut.options.topBar.background.clipToBounds = @(0);
76
+	[self.uut viewDidLoad];
77
+
78
+	XCTAssertFalse(self.uut.navigationController.navigationBar.clipsToBounds);
79
+}
80
+
42 81
 
43 82
 @end

+ 8
- 64
lib/ios/ReactNativeNavigationTests/RNNRootViewControllerTest.m View File

@@ -48,8 +48,8 @@
48 48
 	layoutInfo.componentId = self.componentId;
49 49
 	layoutInfo.name = self.pageName;
50 50
 	
51
-	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] initWithOptions:self.options];
52
-	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:self.creator eventEmitter:self.emitter isExternalComponent:NO presenter:presenter];
51
+	RNNViewControllerPresenter* presenter = [[RNNViewControllerPresenter alloc] init];
52
+	self.uut = [[RNNRootViewController alloc] initWithLayoutInfo:layoutInfo rootViewCreator:self.creator eventEmitter:self.emitter presenter:presenter options:self.options];
53 53
 }
54 54
 
55 55
 -(void)testTopBarBackgroundColor_validColor{
@@ -70,7 +70,6 @@
70 70
 }
71 71
 
72 72
 - (void)testStatusBarHidden_default {
73
-	__unused RNNNavigationController* nav = [self createNavigationController];
74 73
 	[self.uut viewWillAppear:false];
75 74
 
76 75
 	XCTAssertFalse([self.uut prefersStatusBarHidden]);
@@ -78,7 +77,6 @@
78 77
 
79 78
 - (void)testStatusBarVisible_false {
80 79
 	self.options.statusBar.visible = @(0);
81
-	__unused RNNNavigationController* nav = [self createNavigationController];
82 80
 	[self.uut viewWillAppear:false];
83 81
 
84 82
 	XCTAssertTrue([self.uut prefersStatusBarHidden]);
@@ -86,7 +84,6 @@
86 84
 
87 85
 - (void)testStatusBarVisible_true {
88 86
 	self.options.statusBar.visible = @(1);
89
-	__unused RNNNavigationController* nav = [self createNavigationController];
90 87
 	[self.uut viewWillAppear:false];
91 88
 	
92 89
 	XCTAssertFalse([self.uut prefersStatusBarHidden]);
@@ -95,7 +92,6 @@
95 92
 - (void)testStatusBarHideWithTopBar_false {
96 93
 	self.options.statusBar.hideWithTopBar = @(0);
97 94
 	self.options.topBar.visible = @(0);
98
-	__unused RNNNavigationController* nav = [self createNavigationController];
99 95
 	[self.uut viewWillAppear:false];
100 96
 
101 97
 	XCTAssertFalse([self.uut prefersStatusBarHidden]);
@@ -105,6 +101,7 @@
105 101
 	self.options.statusBar.hideWithTopBar = @(1);
106 102
 	self.options.topBar.visible = @(0);
107 103
 	__unused RNNNavigationController* nav = [self createNavigationController];
104
+
108 105
 	[self.uut viewWillAppear:false];
109 106
 
110 107
 	XCTAssertTrue([self.uut prefersStatusBarHidden]);
@@ -113,15 +110,12 @@
113 110
 -(void)testTitle_string{
114 111
 	NSString* title =@"some title";
115 112
 	self.options.topBar.title.text = title;
116
-	__unused RNNNavigationController* nav = [self createNavigationController];
117 113
 
118 114
 	[self.uut viewWillAppear:false];
119 115
 	XCTAssertTrue([self.uut.navigationItem.title isEqual:title]);
120 116
 }
121 117
 
122 118
 -(void)testTitle_default{
123
-	__unused RNNNavigationController* nav = [self createNavigationController];
124
-
125 119
 	[self.uut viewWillAppear:false];
126 120
 	XCTAssertNil(self.uut.navigationItem.title);
127 121
 }
@@ -143,23 +137,6 @@
143 137
 	XCTAssertTrue([self.uut.view.backgroundColor isEqual:expectedColor]);
144 138
 }
145 139
 
146
--(void)testPopGestureEnabled_true{
147
-	NSNumber* popGestureEnabled = @(1);
148
-	self.options.popGesture = popGestureEnabled;
149
-	__unused RNNNavigationController* nav = [self createNavigationController];
150
-	[self.uut viewWillAppear:false];
151
-	XCTAssertTrue(self.uut.navigationController.interactivePopGestureRecognizer.enabled);
152
-}
153
-
154
--(void)testPopGestureEnabled_false{
155
-	NSNumber* popGestureEnabled = @(0);
156
-	self.options.popGesture = popGestureEnabled;
157
-	__unused RNNNavigationController* nav = [self createNavigationController];
158
-	
159
-	[self.uut viewWillAppear:false];
160
-	XCTAssertFalse(self.uut.navigationController.interactivePopGestureRecognizer.enabled);
161
-}
162
-
163 140
 -(void)testTopBarTextFontFamily_validFont{
164 141
 	NSString* inputFont = @"HelveticaNeue";
165 142
 	__unused RNNNavigationController* nav = [self createNavigationController];
@@ -219,21 +196,18 @@
219 196
 }
220 197
 
221 198
 -(void)testTopBarLargeTitle_default {
222
-	__unused RNNNavigationController* nav = [self createNavigationController];
223 199
 	[self.uut viewWillAppear:false];
224 200
 	
225 201
 	XCTAssertEqual(self.uut.navigationItem.largeTitleDisplayMode,  UINavigationItemLargeTitleDisplayModeNever);
226 202
 }
227 203
 -(void)testTopBarLargeTitle_true {
228 204
 	self.options.topBar.largeTitle.visible = @(1);
229
-	__unused RNNNavigationController* nav = [self createNavigationController];
230 205
 	[self.uut viewWillAppear:false];
231 206
 	
232 207
 	XCTAssertEqual(self.uut.navigationItem.largeTitleDisplayMode, UINavigationItemLargeTitleDisplayModeAlways);
233 208
 }
234 209
 -(void)testTopBarLargeTitle_false {
235 210
 	self.options.topBar.largeTitle.visible  = @(0);
236
-	__unused RNNNavigationController* nav = [self createNavigationController];
237 211
 	[self.uut viewWillAppear:false];
238 212
 	
239 213
 	XCTAssertEqual(self.uut.navigationItem.largeTitleDisplayMode, UINavigationItemLargeTitleDisplayModeNever);
@@ -246,6 +220,7 @@
246 220
 	__unused RNNNavigationController* nav = [self createNavigationController];
247 221
 	[self.uut viewWillAppear:false];
248 222
 	UIFont* expectedFont = [UIFont systemFontOfSize:15];
223
+
249 224
 	XCTAssertTrue([self.uut.navigationController.navigationBar.largeTitleTextAttributes[@"NSFont"] isEqual:expectedFont]);
250 225
 }
251 226
 
@@ -482,6 +457,7 @@
482 457
 
483 458
 	self.options.topBar.leftButtons = @[@{@"id": @"testId", @"text": @"test", @"enabled": @false, @"buttonColor": inputColor, @"buttonFontSize": @22, @"buttonFontWeight": @"800"}];
484 459
 	__unused RNNNavigationController* nav = [self createNavigationController];
460
+
485 461
 	[self.uut viewWillAppear:false];
486 462
 
487 463
 	RNNUIBarButtonItem* button = (RNNUIBarButtonItem*)[nav.topViewController.navigationItem.leftBarButtonItems objectAtIndex:0];
@@ -513,7 +489,6 @@
513 489
 -(void)testStatusBarBlurOn {
514 490
 	NSNumber* statusBarBlurInput = @(1);
515 491
 	self.options.statusBar.blur = statusBarBlurInput;
516
-	__unused RNNNavigationController* nav = [self createNavigationController];
517 492
 	[self.uut viewWillAppear:false];
518 493
 	XCTAssertNotNil([self.uut.view viewWithTag:BLUR_STATUS_TAG]);
519 494
 }
@@ -521,13 +496,11 @@
521 496
 -(void)testStatusBarBlurOff {
522 497
 	NSNumber* statusBarBlurInput = @(0);
523 498
 	self.options.statusBar.blur = statusBarBlurInput;
524
-	__unused RNNNavigationController* nav = [self createNavigationController];
525 499
 	[self.uut viewWillAppear:false];
526 500
 	XCTAssertNil([self.uut.view viewWithTag:BLUR_STATUS_TAG]);
527 501
 }
528 502
 
529 503
 - (void)testTabBarHidden_default {
530
-	__unused RNNNavigationController* nav = [self createNavigationController];
531 504
 	[self.uut viewWillAppear:false];
532 505
 
533 506
 	XCTAssertFalse([self.uut hidesBottomBarWhenPushed]);
@@ -536,7 +509,6 @@
536 509
 
537 510
 - (void)testTabBarHidden_true {
538 511
 	self.options.bottomTabs.visible = @(0);
539
-	__unused RNNNavigationController* nav = [self createNavigationController];
540 512
 	[self.uut viewWillAppear:false];
541 513
 
542 514
 	XCTAssertTrue([self.uut hidesBottomBarWhenPushed]);
@@ -544,7 +516,6 @@
544 516
 
545 517
 - (void)testTabBarHidden_false {
546 518
 	self.options.bottomTabs.visible = @(1);
547
-	__unused RNNNavigationController* nav = [self createNavigationController];
548 519
 	[self.uut viewWillAppear:false];
549 520
 
550 521
 	XCTAssertFalse([self.uut hidesBottomBarWhenPushed]);
@@ -575,23 +546,13 @@
575 546
 -(void)testBackgroundImage {
576 547
 	UIImage* backgroundImage = [[UIImage alloc] init];
577 548
 	self.options.backgroundImage = backgroundImage;
578
-	__unused RNNNavigationController* nav = [self createNavigationController];
579 549
 	[self.uut viewWillAppear:false];
580 550
 
581 551
 	XCTAssertTrue([[(UIImageView*)self.uut.view.subviews[0] image] isEqual:backgroundImage]);
582 552
 }
583 553
 
584
--(void)testRootBackgroundImage {
585
-	UIImage* rootBackgroundImage = [[UIImage alloc] init];
586
-	self.options.rootBackgroundImage = rootBackgroundImage;
587
-	__unused RNNNavigationController* nav = [self createNavigationController];
588
-	[self.uut viewWillAppear:false];
589
-	XCTAssertTrue([[(UIImageView*)self.uut.navigationController.view.subviews[0] image] isEqual:rootBackgroundImage]);
590
-}
591
-
592 554
 -(void)testTopBarDrawUnder_true {
593 555
 	self.options.topBar.drawBehind = @(1);
594
-	__unused RNNNavigationController* nav = [self createNavigationController];
595 556
 	[self.uut viewWillAppear:false];
596 557
 
597 558
 	XCTAssertTrue(self.uut.edgesForExtendedLayout & UIRectEdgeTop);
@@ -599,7 +560,6 @@
599 560
 
600 561
 -(void)testTopBarDrawUnder_false {
601 562
 	self.options.topBar.drawBehind = @(0);
602
-	__unused RNNNavigationController* nav = [self createNavigationController];
603 563
 	[self.uut viewWillAppear:false];
604 564
 
605 565
 	XCTAssertFalse(self.uut.edgesForExtendedLayout & UIRectEdgeTop);
@@ -607,7 +567,6 @@
607 567
 
608 568
 -(void)testBottomTabsDrawUnder_true {
609 569
 	self.options.bottomTabs.drawBehind = @(1);
610
-	__unused RNNNavigationController* nav = [self createNavigationController];
611 570
 	[self.uut viewWillAppear:false];
612 571
 
613 572
 	XCTAssertTrue(self.uut.edgesForExtendedLayout & UIRectEdgeBottom);
@@ -615,7 +574,6 @@
615 574
 
616 575
 -(void)testBottomTabsDrawUnder_false {
617 576
 	self.options.bottomTabs.drawBehind = @(0);
618
-	__unused RNNNavigationController* nav = [self createNavigationController];
619 577
 	[self.uut viewWillAppear:false];
620 578
 
621 579
 	XCTAssertFalse(self.uut.edgesForExtendedLayout & UIRectEdgeBottom);
@@ -714,31 +672,17 @@
714 672
 	XCTAssertTrue([attributes[@"NSFont"] isEqual:expectedFont]);
715 673
 }
716 674
 
717
-- (void)testTopBarBackgroundClipToBounds_true {
718
-	self.options.topBar.background.clipToBounds = @(1);
719
-	__unused RNNNavigationController* nav = [self createNavigationController];
720
-	[self.uut viewWillAppear:false];
721
-
722
-	XCTAssertTrue(self.uut.navigationController.navigationBar.clipsToBounds);
723
-}
724
-
725
-- (void)testTopBarBackgroundClipToBounds_false {
726
-	__unused RNNNavigationController* nav = [self createNavigationController];
727
-	[self.uut viewWillAppear:false];
728
-
729
-	XCTAssertFalse(self.uut.navigationController.navigationBar.clipsToBounds);
730
-}
731
-
732 675
 - (void)testWillMoveToParent_shouldPassOptionsToParent {
733 676
 	__unused RNNNavigationController* nav = [self createNavigationController];
734
-	self.uut.presenter.options.topBar.visible = @(0);
677
+	self.uut.options.topBar.visible = @(0);
735 678
 	[self.uut willMoveToParentViewController:nav];
736 679
 	XCTAssertTrue(nav.navigationBarHidden);
737 680
 }
738 681
 
739 682
 - (RNNNavigationController *)createNavigationController {
740 683
 	RNNNavigationController* nav = [[RNNNavigationController alloc] initWithRootViewController:self.uut];
741
-	nav.presenter = [[RNNNavigationControllerPresenter alloc] initWithOptions:[[RNNNavigationOptions alloc] initWithDict:@{}]];
684
+	nav.options = [[RNNNavigationOptions alloc] initWithDict:@{}];
685
+	nav.presenter = [[RNNNavigationControllerPresenter alloc] init];
742 686
 	
743 687
 	return nav;
744 688
 }