Browse Source

Support centered bottomTab icons using bottomTabs.titleDisplayMode option (#5667)

* Support centered bottomTab icons using bottomTabs.titleDisplayMode option

* Improve readability, better namings

* Rename setTabItemImagesCentered to centerTabItems
Yogev Ben David 5 years ago
parent
commit
26d3d82190
No account linked to committer's email address

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

17
 @property (nonatomic, strong) Text* currentTabId;
17
 @property (nonatomic, strong) Text* currentTabId;
18
 @property (nonatomic, strong) Text* barStyle;
18
 @property (nonatomic, strong) Text* barStyle;
19
 @property (nonatomic, strong) Text* fontFamily;
19
 @property (nonatomic, strong) Text* fontFamily;
20
+@property (nonatomic, strong) Text* titleDisplayMode;
20
 
21
 
21
 @end
22
 @end

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

20
 	self.currentTabId = [TextParser parse:dict key:@"currentTabId"];
20
 	self.currentTabId = [TextParser parse:dict key:@"currentTabId"];
21
 	self.barStyle = [TextParser parse:dict key:@"barStyle"];
21
 	self.barStyle = [TextParser parse:dict key:@"barStyle"];
22
 	self.fontFamily = [TextParser parse:dict key:@"fontFamily"];
22
 	self.fontFamily = [TextParser parse:dict key:@"fontFamily"];
23
+	self.titleDisplayMode = [TextParser parse:dict key:@"titleDisplayMode"];
23
 	
24
 	
24
 	return self;
25
 	return self;
25
 }
26
 }

+ 3
- 0
lib/ios/RNNBottomTabsPresenter.m View File

9
     UITabBarController *bottomTabs = self.boundViewController;
9
     UITabBarController *bottomTabs = self.boundViewController;
10
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
10
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
11
     [bottomTabs setCurrentTabIndex:[withDefault.bottomTabs.currentTabIndex getWithDefaultValue:0]];
11
     [bottomTabs setCurrentTabIndex:[withDefault.bottomTabs.currentTabIndex getWithDefaultValue:0]];
12
+	if ([[withDefault.bottomTabs.titleDisplayMode getWithDefaultValue:@"alwaysShow"] isEqualToString:@"alwaysHide"]) {
13
+		[bottomTabs centerTabItems];
14
+	}
12
 }
15
 }
13
 
16
 
14
 - (void)applyOptions:(RNNNavigationOptions *)options {
17
 - (void)applyOptions:(RNNNavigationOptions *)options {

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

50
 		390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
50
 		390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
51
 		4534E72520CB6724009F8185 /* RNNLargeTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */; };
51
 		4534E72520CB6724009F8185 /* RNNLargeTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */; };
52
 		4534E72620CB6724009F8185 /* RNNLargeTitleOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */; };
52
 		4534E72620CB6724009F8185 /* RNNLargeTitleOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */; };
53
+		5008641223856A2D00A55BE9 /* UITabBar+utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5008641023856A2C00A55BE9 /* UITabBar+utils.m */; };
53
 		501214C9217741A000435148 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 501214C8217741A000435148 /* libOCMock.a */; };
54
 		501214C9217741A000435148 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 501214C8217741A000435148 /* libOCMock.a */; };
54
 		501223D72173590F000F5F98 /* RNNStackPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 501223D52173590F000F5F98 /* RNNStackPresenter.h */; };
55
 		501223D72173590F000F5F98 /* RNNStackPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 501223D52173590F000F5F98 /* RNNStackPresenter.h */; };
55
 		501223D82173590F000F5F98 /* RNNStackPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 501223D62173590F000F5F98 /* RNNStackPresenter.m */; };
56
 		501223D82173590F000F5F98 /* RNNStackPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 501223D62173590F000F5F98 /* RNNStackPresenter.m */; };
399
 		390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
400
 		390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
400
 		4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNLargeTitleOptions.h; sourceTree = "<group>"; };
401
 		4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNLargeTitleOptions.h; sourceTree = "<group>"; };
401
 		4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNLargeTitleOptions.m; sourceTree = "<group>"; };
402
 		4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNLargeTitleOptions.m; sourceTree = "<group>"; };
403
+		5008641023856A2C00A55BE9 /* UITabBar+utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UITabBar+utils.m"; sourceTree = "<group>"; };
404
+		5008641123856A2D00A55BE9 /* UITabBar+utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UITabBar+utils.h"; sourceTree = "<group>"; };
402
 		501214C8217741A000435148 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
405
 		501214C8217741A000435148 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
403
 		501223D52173590F000F5F98 /* RNNStackPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNStackPresenter.h; sourceTree = "<group>"; };
406
 		501223D52173590F000F5F98 /* RNNStackPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNStackPresenter.h; sourceTree = "<group>"; };
404
 		501223D62173590F000F5F98 /* RNNStackPresenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNStackPresenter.m; sourceTree = "<group>"; };
407
 		501223D62173590F000F5F98 /* RNNStackPresenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNStackPresenter.m; sourceTree = "<group>"; };
733
 				50706E6C20CE7CA5003345C3 /* UIImage+tint.m */,
736
 				50706E6C20CE7CA5003345C3 /* UIImage+tint.m */,
734
 				506317A8220B547400B26FC3 /* UIImage+insets.h */,
737
 				506317A8220B547400B26FC3 /* UIImage+insets.h */,
735
 				506317A9220B547400B26FC3 /* UIImage+insets.m */,
738
 				506317A9220B547400B26FC3 /* UIImage+insets.m */,
739
+				5008641123856A2D00A55BE9 /* UITabBar+utils.h */,
740
+				5008641023856A2C00A55BE9 /* UITabBar+utils.m */,
736
 				5038A372216CDDB6009280BC /* UIViewController+SideMenuController.h */,
741
 				5038A372216CDDB6009280BC /* UIViewController+SideMenuController.h */,
737
 				5038A373216CDDB6009280BC /* UIViewController+SideMenuController.m */,
742
 				5038A373216CDDB6009280BC /* UIViewController+SideMenuController.m */,
738
 				5038A3BF216E1E66009280BC /* RNNFontAttributesCreator.h */,
743
 				5038A3BF216E1E66009280BC /* RNNFontAttributesCreator.h */,
1604
 				501224072173592D000F5F98 /* RNNBottomTabsPresenter.m in Sources */,
1609
 				501224072173592D000F5F98 /* RNNBottomTabsPresenter.m in Sources */,
1605
 				50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */,
1610
 				50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */,
1606
 				5039559C2174867000B0A663 /* DoubleParser.m in Sources */,
1611
 				5039559C2174867000B0A663 /* DoubleParser.m in Sources */,
1612
+				5008641223856A2D00A55BE9 /* UITabBar+utils.m in Sources */,
1607
 				E5F6C3A822DB4D0F0093C2CE /* UIView+Utils.m in Sources */,
1613
 				E5F6C3A822DB4D0F0093C2CE /* UIView+Utils.m in Sources */,
1608
 				5049593F216F5D73006D2B81 /* BoolParser.m in Sources */,
1614
 				5049593F216F5D73006D2B81 /* BoolParser.m in Sources */,
1609
 				50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,
1615
 				50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,

+ 16
- 0
lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m View File

54
     [self.boundViewController verify];
54
     [self.boundViewController verify];
55
 }
55
 }
56
 
56
 
57
+- (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages {
58
+	RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
59
+	initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"];
60
+	[[self.boundViewController reject] centerTabItems];
61
+	[self.uut applyOptionsOnInit:initialOptions];
62
+	[self.boundViewController verify];
63
+}
64
+
65
+- (void)testApplyOptions_shouldApplyOptionsOnInit_alwaysHide_shouldCenterTabImages {
66
+	RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
67
+	initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysHide"];
68
+	[[self.boundViewController expect] centerTabItems];
69
+	[self.uut applyOptionsOnInit:initialOptions];
70
+	[self.boundViewController verify];
71
+}
72
+
57
 - (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
73
 - (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
58
     id uut = [self uut];
74
     id uut = [self uut];
59
     [[uut expect] applyDotIndicator];
75
     [[uut expect] applyDotIndicator];

+ 9
- 1
lib/ios/ReactNativeNavigationTests/UITabBarController+RNNOptionsTest.m View File

1
 #import <XCTest/XCTest.h>
1
 #import <XCTest/XCTest.h>
2
-#import <OCMockObject.h>
2
+#import <OCMock/OCMock.h>
3
 #import "UITabBarController+RNNOptions.h"
3
 #import "UITabBarController+RNNOptions.h"
4
+#import "UITabBar+utils.h"
4
 
5
 
5
 @interface UITabBarController_RNNOptionsTest : XCTestCase
6
 @interface UITabBarController_RNNOptionsTest : XCTestCase
6
 
7
 
13
 - (void)setUp {
14
 - (void)setUp {
14
     [super setUp];
15
     [super setUp];
15
 	self.uut = [OCMockObject partialMockForObject:[UITabBarController new]];
16
 	self.uut = [OCMockObject partialMockForObject:[UITabBarController new]];
17
+	OCMStub([self.uut tabBar]).andReturn([OCMockObject partialMockForObject:[UITabBar new]]);
16
 }
18
 }
17
 
19
 
18
 - (void)test_tabBarTranslucent_true {
20
 - (void)test_tabBarTranslucent_true {
39
 	XCTAssertFalse(self.uut.tabBar.clipsToBounds);
41
 	XCTAssertFalse(self.uut.tabBar.clipsToBounds);
40
 }
42
 }
41
 
43
 
44
+- (void)test_centerTabItems {
45
+	[[(id)self.uut.tabBar expect] centerTabItems];
46
+	[self.uut centerTabItems];
47
+	[(id)self.uut.tabBar verify];
48
+}
49
+
42
 - (void)test_tabBarBackgroundColor {
50
 - (void)test_tabBarBackgroundColor {
43
 	UIColor* tabBarBackgroundColor = [UIColor redColor];
51
 	UIColor* tabBarBackgroundColor = [UIColor redColor];
44
 
52
 

+ 7
- 0
lib/ios/UITabBar+utils.h View File

1
+#import <UIKit/UIKit.h>
2
+
3
+@interface UITabBar (utils)
4
+
5
+- (void)centerTabItems;
6
+
7
+@end

+ 60
- 0
lib/ios/UITabBar+utils.m View File

1
+#import "UITabBar+utils.h"
2
+#import <objc/runtime.h>
3
+
4
+#define BADGE_OFFSET 0.2
5
+#define IMAGE_VIEW_TAG 1
6
+
7
+typedef void (*UITabBarButton_layoutSubviews__IMP)(void);
8
+static UITabBarButton_layoutSubviews__IMP original_UITabBarButton_layoutSubviews;
9
+
10
+@implementation UITabBar (utils)
11
+
12
+- (void)centerTabItems {
13
+	[self removeTabBarItemTitles];
14
+	[self swizzleUITabBarButton];
15
+}
16
+
17
+- (void)removeTabBarItemTitles {
18
+	for (UITabBarItem* item in self.items) {
19
+		item.title = nil;
20
+	}
21
+}
22
+
23
+- (void)swizzleUITabBarButton {
24
+	static dispatch_once_t onceToken;
25
+	dispatch_once(&onceToken, ^{
26
+		[[self class] swizzleUITabBarButtonLayoutSubviews];
27
+	});
28
+}
29
+
30
++ (void)swizzleUITabBarButtonLayoutSubviews {
31
+	Class UITabBarButtonClass = NSClassFromString(@"UITabBarButton");
32
+	
33
+	SEL layoutSubviewsSEL = @selector(layoutSubviews);
34
+	Method layoutSubviewsMethod = class_getInstanceMethod(UITabBarButtonClass, layoutSubviewsSEL);
35
+	IMP layoutSubviewsIMP = method_getImplementation(layoutSubviewsMethod);
36
+	
37
+	original_UITabBarButton_layoutSubviews = layoutSubviewsIMP;
38
+	
39
+	SEL swizzleUITabBarButton_layoutSubviewsSEL = @selector(swizzleUITabBarButton_layoutSubviews);
40
+	Method swizzleUITabBarButton_layoutSubviewsMethod = class_getInstanceMethod(self, swizzleUITabBarButton_layoutSubviewsSEL);
41
+	
42
+	method_exchangeImplementations(layoutSubviewsMethod, swizzleUITabBarButton_layoutSubviewsMethod);
43
+}
44
+
45
+- (void)swizzleUITabBarButton_layoutSubviews {
46
+	original_UITabBarButton_layoutSubviews();
47
+	for (UIView *subView in self.subviews) {
48
+		if ([subView isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")]) {
49
+			subView.center = CGPointMake(subView.center.x, subView.superview.frame.size.height / 2);
50
+			subView.tag = IMAGE_VIEW_TAG;
51
+		}
52
+		
53
+		if ([subView isKindOfClass:NSClassFromString(@"_UIBadgeView")]) {
54
+			UIView* imageView = [subView.superview viewWithTag:IMAGE_VIEW_TAG];
55
+			subView.frame = CGRectMake(subView.frame.origin.x, (imageView.frame.origin.y + imageView.frame.size.height * BADGE_OFFSET) - subView.frame.size.height / 2, subView.frame.size.width, subView.frame.size.height);
56
+		}
57
+	}
58
+}
59
+
60
+@end

+ 2
- 0
lib/ios/UITabBarController+RNNOptions.h View File

18
 
18
 
19
 - (void)setTabBarVisible:(BOOL)visible animated:(BOOL)animated;
19
 - (void)setTabBarVisible:(BOOL)visible animated:(BOOL)animated;
20
 
20
 
21
+- (void)centerTabItems;
22
+
21
 @end
23
 @end

+ 5
- 0
lib/ios/UITabBarController+RNNOptions.m View File

1
 #import "UITabBarController+RNNOptions.h"
1
 #import "UITabBarController+RNNOptions.h"
2
 #import "RNNBottomTabsController.h"
2
 #import "RNNBottomTabsController.h"
3
+#import "UITabBar+utils.h"
3
 
4
 
4
 @implementation UITabBarController (RNNOptions)
5
 @implementation UITabBarController (RNNOptions)
5
 
6
 
74
 	}
75
 	}
75
 }
76
 }
76
 
77
 
78
+- (void)centerTabItems {
79
+	[self.tabBar centerTabItems];
80
+}
81
+
77
 - (void)forEachTab:(void (^)(UIView *, UIViewController * tabViewController, int tabIndex))performOnTab {
82
 - (void)forEachTab:(void (^)(UIView *, UIViewController * tabViewController, int tabIndex))performOnTab {
78
     int tabIndex = 0;
83
     int tabIndex = 0;
79
     for (UIView * tab in self.tabBar.subviews) {
84
     for (UIView * tab in self.tabBar.subviews) {