Browse Source

Fix mergeOptions

Merging TopBar title, buttons and status bar options is broken on iOS in 3.1.1.
Parent options aren't resolved properly and default options aren't regarded when they should be.
Guy Carmeli 5 years ago
parent
commit
5409a625d9

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

@@ -31,4 +31,8 @@ typedef void (^RNNReactViewReadyCompletionBlock)(void);
31 31
 - (void)renderComponents:(RNNNavigationOptions *)options perform:(RNNReactViewReadyCompletionBlock)readyBlock;
32 32
 
33 33
 - (void)viewDidLayoutSubviews;
34
+
35
+- (UIStatusBarStyle)getStatusBarStyle:(RNNNavigationOptions *)resolvedOptions;
36
+
37
+- (BOOL)isStatusBarVisibility:(UINavigationController *)stack resolvedOptions:(RNNNavigationOptions *)resolvedOptions;
34 38
 @end

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

@@ -164,4 +164,25 @@
164 164
 - (void)applyDotIndicator:(UIViewController *)child {
165 165
     [[self dotIndicatorPresenter] apply:child:[child resolveOptions].bottomTab.dotIndicator];
166 166
 }
167
+
168
+- (UIStatusBarStyle)getStatusBarStyle:(RNNNavigationOptions *)resolvedOptions {
169
+    RNNNavigationOptions *withDefault = [resolvedOptions withDefault:[self defaultOptions]];
170
+    if ([[withDefault.statusBar.style getWithDefaultValue:@"default"] isEqualToString:@"light"]) {
171
+        return UIStatusBarStyleLightContent;
172
+    } else {
173
+        return UIStatusBarStyleDefault;
174
+    }
175
+}
176
+
177
+- (BOOL)isStatusBarVisibility:(UINavigationController *)stack resolvedOptions:(RNNNavigationOptions *)resolvedOptions {
178
+    RNNNavigationOptions *withDefault = [resolvedOptions withDefault:[self defaultOptions]];
179
+    if (withDefault.statusBar.visible.hasValue) {
180
+        return ![withDefault.statusBar.visible get];
181
+    } else if ([withDefault.statusBar.hideWithTopBar getWithDefaultValue:NO]) {
182
+        return stack.isNavigationBarHidden;
183
+    }
184
+    return NO;
185
+}
186
+
187
+
167 188
 @end

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

@@ -32,7 +32,7 @@ const NSInteger TOP_BAR_TRANSPARENT_TAG = 78264803;
32 32
 }
33 33
 
34 34
 - (UIStatusBarStyle)preferredStatusBarStyle {
35
-	return self.getCurrentChild.preferredStatusBarStyle;
35
+	return [_presenter getStatusBarStyle:self.resolveOptions];
36 36
 }
37 37
 
38 38
 - (UIModalPresentationStyle)modalPresentationStyle {

+ 7
- 4
lib/ios/RNNNavigationControllerPresenter.m View File

@@ -120,12 +120,15 @@
120 120
 	}
121 121
 	
122 122
 
123
-	RNNLargeTitleOptions *largteTitleOptions = newOptions.topBar.largeTitle;
124
-	if (largteTitleOptions.color.hasValue || largteTitleOptions.fontSize.hasValue || largteTitleOptions.fontFamily.hasValue) {
123
+	RNNLargeTitleOptions *largeTitleOptions = newOptions.topBar.largeTitle;
124
+	if (largeTitleOptions.color.hasValue || largeTitleOptions.fontSize.hasValue || largeTitleOptions.fontFamily.hasValue) {
125 125
 		[navigationController rnn_setNavigationBarLargeTitleFontFamily:[newOptions.topBar.largeTitle.fontFamily getWithDefaultValue:nil] fontSize:[newOptions.topBar.largeTitle.fontSize getWithDefaultValue:nil] color:[newOptions.topBar.largeTitle.color getWithDefaultValue:nil]];
126 126
 	}
127
-	
128
-	[navigationController rnn_setNavigationBarFontFamily:[newOptions.topBar.title.fontFamily getWithDefaultValue:nil] fontSize:[newOptions.topBar.title.fontSize getWithDefaultValue:nil] color:[newOptions.topBar.title.color getWithDefaultValue:nil]];
127
+
128
+	RNNNavigationOptions * withDefault = (RNNNavigationOptions *) [[newOptions mergeInOptions:currentOptions] withDefault:[self defaultOptions]];
129
+	[navigationController rnn_setNavigationBarFontFamily:[withDefault.topBar.title.fontFamily getWithDefaultValue:nil]
130
+												fontSize:[withDefault.topBar.title.fontSize getWithDefaultValue:nil]
131
+												   color:[withDefault.topBar.title.color getWithDefaultValue:nil]];
129 132
 	
130 133
 	if (newOptions.topBar.component.name.hasValue) {
131 134
 		[self setCustomNavigationBarView:newOptions perform:nil];

+ 2
- 12
lib/ios/RNNRootViewController.m View File

@@ -116,21 +116,11 @@
116 116
 }
117 117
 
118 118
 - (BOOL)prefersStatusBarHidden {
119
-	if (self.resolveOptions.statusBar.visible.hasValue) {
120
-		return ![self.resolveOptions.statusBar.visible get];
121
-	} else if ([self.resolveOptions.statusBar.hideWithTopBar getWithDefaultValue:NO]) {
122
-		return self.navigationController.isNavigationBarHidden;
123
-	}
124
-	
125
-	return NO;
119
+	return [_presenter isStatusBarVisibility:self.navigationController resolvedOptions:self.resolveOptions];
126 120
 }
127 121
 
128 122
 - (UIStatusBarStyle)preferredStatusBarStyle {
129
-	if ([[self.resolveOptions.statusBar.style getWithDefaultValue:@"default"] isEqualToString:@"light"]) {
130
-		return UIStatusBarStyleLightContent;
131
-	} else {
132
-		return UIStatusBarStyleDefault;
133
-	}
123
+	return [_presenter getStatusBarStyle:[self resolveOptions]];
134 124
 }
135 125
 
136 126
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {

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

@@ -40,7 +40,7 @@
40 40
 }
41 41
 
42 42
 - (UIStatusBarStyle)preferredStatusBarStyle {
43
-	return self.child.preferredStatusBarStyle;
43
+	return [[self presenter] getStatusBarStyle:[self resolveOptions]];
44 44
 }
45 45
 
46 46
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {

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

@@ -44,7 +44,7 @@
44 44
 }
45 45
 
46 46
 - (UIStatusBarStyle)preferredStatusBarStyle {
47
-	return self.selectedViewController.preferredStatusBarStyle;
47
+	return [[self presenter] getStatusBarStyle:self.resolveOptions];
48 48
 }
49 49
 
50 50
 #pragma mark UITabBarControllerDelegate

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

@@ -79,7 +79,8 @@
79 79
 
80 80
 - (void)mergeOptions:(RNNNavigationOptions *)newOptions currentOptions:(RNNNavigationOptions *)currentOptions {
81 81
 	[super mergeOptions:newOptions currentOptions:currentOptions];
82
-	
82
+	RNNNavigationOptions * withDefault	= (RNNNavigationOptions *) [[currentOptions overrideOptions:newOptions] withDefault:[self defaultOptions]];
83
+
83 84
 	UIViewController* viewController = self.boundViewController;
84 85
 	
85 86
 	if (newOptions.backgroundImage.hasValue) {
@@ -135,7 +136,7 @@
135 136
 	}
136 137
 	
137 138
 	if (newOptions.statusBar.style.hasValue) {
138
-		[viewController rnn_setStatusBarStyle:newOptions.statusBar.style.get animated:[newOptions.statusBar.animate getWithDefaultValue:YES]];
139
+		[viewController rnn_setStatusBarStyle:newOptions.statusBar.style.get animated:[withDefault.statusBar.animate getWithDefaultValue:YES]];
139 140
 	}
140 141
 	
141 142
 	if (newOptions.topBar.backButton.visible.hasValue) {
@@ -143,17 +144,17 @@
143 144
 	}
144 145
 	
145 146
 	if (newOptions.topBar.leftButtons || newOptions.topBar.rightButtons) {
146
-		RNNNavigationOptions* buttonsResolvedOptions = (RNNNavigationOptions *)[currentOptions overrideOptions:newOptions];
147
-		[_navigationButtons applyLeftButtons:newOptions.topBar.leftButtons rightButtons:newOptions.topBar.rightButtons defaultLeftButtonStyle:buttonsResolvedOptions.topBar.leftButtonStyle defaultRightButtonStyle:buttonsResolvedOptions.topBar.rightButtonStyle];
147
+		[_navigationButtons applyLeftButtons:newOptions.topBar.leftButtons rightButtons:newOptions.topBar.rightButtons defaultLeftButtonStyle:withDefault.topBar.leftButtonStyle defaultRightButtonStyle:withDefault.topBar.rightButtonStyle];
148 148
 	}
149 149
 	
150
+
150 151
 	if (newOptions.overlay.interceptTouchOutside.hasValue) {
151 152
 		RCTRootView* rootView = (RCTRootView*)viewController.view;
152 153
 		rootView.passThroughTouches = !newOptions.overlay.interceptTouchOutside.get;
153 154
 	}
154
-	
155
-	[self setTitleViewWithSubtitle:(RNNNavigationOptions *)[currentOptions overrideOptions:newOptions]];
156
-	
155
+
156
+	[self setTitleViewWithSubtitle:withDefault];
157
+
157 158
 	if (newOptions.topBar.title.component.name.hasValue) {
158 159
 		[self setCustomNavigationTitleView:newOptions perform:nil];
159 160
 	} else {

+ 30
- 0
lib/ios/ReactNativeNavigationTests/RNNBasePresenterTest.m View File

@@ -60,4 +60,34 @@
60 60
     [self.mockBoundViewController verify];
61 61
 }
62 62
 
63
+- (void)testGetPreferredStatusBarStyle_returnLightIfLight {
64
+    RNNNavigationOptions * lightOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
65
+    lightOptions.statusBar.style = [[Text alloc] initWithValue:@"light"];
66
+
67
+    XCTAssertEqual([_uut getStatusBarStyle:lightOptions], UIStatusBarStyleLightContent);
68
+}
69
+
70
+- (void)testGetPreferredStatusBarStyle_returnDefaultIfDark {
71
+    RNNNavigationOptions * darkOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
72
+    darkOptions.statusBar.style = [[Text alloc] initWithValue:@"dark"];
73
+
74
+    XCTAssertEqual([_uut getStatusBarStyle:darkOptions], UIStatusBarStyleDefault);
75
+}
76
+
77
+- (void)testGetPreferredStatusBarStyle_returnDefaultIfNil {
78
+    RNNNavigationOptions * options = [[RNNNavigationOptions alloc] initEmptyOptions];
79
+
80
+    XCTAssertEqual([_uut getStatusBarStyle:options], UIStatusBarStyleDefault);
81
+}
82
+
83
+- (void)testGetPreferredStatusBarStyle_considersDefaultOptions {
84
+    RNNNavigationOptions * options = [[RNNNavigationOptions alloc] initEmptyOptions];
85
+    RNNNavigationOptions * lightOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
86
+    lightOptions.statusBar.style = [[Text alloc] initWithValue:@"light"];
87
+    [_uut setDefaultOptions:lightOptions];
88
+
89
+    XCTAssertEqual([_uut getStatusBarStyle:options], UIStatusBarStyleLightContent);
90
+}
91
+
92
+
63 93
 @end

+ 2
- 8
lib/ios/ReactNativeNavigationTests/RNNTabBarControllerTest.m View File

@@ -126,15 +126,9 @@
126 126
 }
127 127
 
128 128
 - (void)testPreferredStatusBarStyle_shouldInvokeSelectedViewControllerPreferredStatusBarStyle {
129
-    [[self.mockChildViewController expect] preferredStatusBarStyle];
129
+    [[self.mockTabBarPresenter expect] getStatusBarStyle:[OCMArg any]];
130 130
     [self.uut preferredStatusBarStyle];
131
-    [self.mockChildViewController verify];
132
-}
133
-
134
-- (void)testPreferredStatusBarStyle_shouldInvokeOnSelectedViewController {
135
-    [[self.mockChildViewController expect] preferredStatusBarStyle];
136
-    [self.uut preferredStatusBarStyle];
137
-    [self.mockChildViewController verify];
131
+    [self.mockTabBarPresenter verify];
138 132
 }
139 133
 
140 134
 - (void)testTabBarControllerDidSelectViewControllerDelegate_shouldInvokeSendBottomTabSelectedEvent {

+ 44
- 7
lib/ios/ReactNativeNavigationTests/UIViewController+LayoutProtocolTest.m View File

@@ -9,7 +9,7 @@
9 9
 
10 10
 @interface UIViewController_LayoutProtocolTest : XCTestCase
11 11
 
12
-@property (nonatomic, retain) UIViewController* uut;
12
+@property (nonatomic, retain) UIViewController * uut;
13 13
 
14 14
 @end
15 15
 
@@ -18,8 +18,8 @@
18 18
 - (void)setUp {
19 19
 	[super setUp];
20 20
 	self.uut = [OCMockObject partialMockForObject:[UIViewController new]];
21
-    self.uut.layoutInfo = [[RNNLayoutInfo alloc] init];
22
-    self.uut.layoutInfo.componentId = @"componentId";
21
+    _uut.layoutInfo = [[RNNLayoutInfo alloc] init];
22
+    _uut.layoutInfo.componentId = @"componentId";
23 23
 }
24 24
 
25 25
 - (void)testInitWithLayoutApplyDefaultOptions {
@@ -53,8 +53,8 @@
53 53
 
54 54
 - (void)testSetBackButtonIcon_withColor_shouldSetTitle {
55 55
 	UIViewController* uut = [UIViewController new];
56
-	UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:uut];
57
-	NSString* title = @"Title";
56
+    UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:uut];
57
+    NSString* title = @"Title";
58 58
 	
59 59
 	[uut rnn_setBackButtonIcon:nil withColor:nil title:title];
60 60
 	XCTAssertEqual(title, uut.navigationItem.backBarButtonItem.title);
@@ -62,8 +62,8 @@
62 62
 
63 63
 - (void)testSetBackButtonIcon_withColor_shouldSetIcon {
64 64
 	UIViewController* uut = [UIViewController new];
65
-	UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:uut];
66
-	UIImage* icon = [UIImage new];
65
+    UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:uut];
66
+    UIImage* icon = [UIImage new];
67 67
 	
68 68
 	[uut rnn_setBackButtonIcon:icon withColor:nil title:nil];
69 69
 	XCTAssertEqual(icon, uut.navigationItem.backBarButtonItem.image);
@@ -99,4 +99,41 @@
99 99
 	XCTAssertEqual([[parent resolveOptions].bottomTab.selectedIconColor get], UIColor.redColor);
100 100
 }
101 101
 
102
+- (void)testMergeOptions_invokedOnParentViewController {
103
+    id parent = [OCMockObject partialMockForObject:[RNNNavigationController new]];
104
+    RNNNavigationOptions * toMerge = [[RNNNavigationOptions alloc] initEmptyOptions];
105
+    [(UIViewController *) [parent expect] mergeOptions:toMerge];
106
+
107
+    RNNNavigationController* uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:nil options:nil defaultOptions:nil presenter:nil eventEmitter:nil childViewControllers:nil];
108
+    [parent addChildViewController:uut];
109
+
110
+    [uut mergeOptions:toMerge];
111
+    [parent verify];
112
+}
113
+
114
+- (void)testMergeOptions_presenterIsInvokedWithResolvedOptions {
115
+    id parent = [OCMockObject partialMockForObject:[RNNNavigationController new]];
116
+    id presenter = [OCMockObject partialMockForObject:[RNNNavigationControllerPresenter new]];
117
+    RNNNavigationOptions * toMerge = [[RNNNavigationOptions alloc] initEmptyOptions];
118
+    toMerge.topBar.title.color = [[Color alloc] initWithValue:[UIColor redColor]];
119
+
120
+    [[presenter expect] mergeOptions:toMerge currentOptions:[OCMArg checkWithBlock:^(id value) {
121
+        RNNNavigationOptions * options = (RNNNavigationOptions *) value;
122
+        XCTAssertEqual([options.topBar.title.text get], @"Initial title");
123
+        XCTAssertEqual([options.bottomTab.text get], @"Child tab text");
124
+        return YES;
125
+    }]];
126
+
127
+    RNNNavigationOptions * childOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
128
+    childOptions.bottomTab.text = [[Text alloc] initWithValue:@"Child tab text"];
129
+    UIViewController* child = [[UIViewController alloc] initWithLayoutInfo:nil creator:nil options:childOptions defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:nil];
130
+    RNNNavigationOptions * initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
131
+    initialOptions.topBar.title.text = [[Text alloc] initWithValue:@"Initial title"];
132
+    RNNNavigationController* uut = [[RNNNavigationController alloc] initWithLayoutInfo:nil creator:nil options:initialOptions defaultOptions:nil presenter:presenter eventEmitter:nil childViewControllers:@[child]];
133
+    [parent addChildViewController:uut];
134
+
135
+	[uut mergeOptions:toMerge];
136
+    [presenter verify];
137
+}
138
+
102 139
 @end

+ 5
- 5
lib/ios/UIViewController+LayoutProtocol.m View File

@@ -28,15 +28,15 @@
28 28
 	return self;
29 29
 }
30 30
 
31
-- (RNNNavigationOptions *)resolveOptions {
32
-    return (RNNNavigationOptions *) [self.options mergeInOptions:self.getCurrentChild.resolveOptions.copy];
33
-}
34
-
35 31
 - (void)mergeOptions:(RNNNavigationOptions *)options {
36
-	[self.presenter mergeOptions:options currentOptions:self.options];
32
+	[self.presenter mergeOptions:options currentOptions:self.resolveOptions];
37 33
 	[self.parentViewController mergeOptions:options];
38 34
 }
39 35
 
36
+- (RNNNavigationOptions *)resolveOptions {
37
+    return (RNNNavigationOptions *) [self.options mergeInOptions:self.getCurrentChild.resolveOptions.copy];
38
+}
39
+
40 40
 - (void)overrideOptions:(RNNNavigationOptions *)options {
41 41
 	[self.options overrideOptions:options];
42 42
 }