Parcourir la 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 il y a 5 ans
Parent
révision
26d3d82190
No account linked to committer's email address

+ 1
- 0
lib/ios/RNNBottomTabsOptions.h Voir le fichier

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

+ 1
- 0
lib/ios/RNNBottomTabsOptions.m Voir le fichier

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

+ 3
- 0
lib/ios/RNNBottomTabsPresenter.m Voir le fichier

@@ -9,6 +9,9 @@
9 9
     UITabBarController *bottomTabs = self.boundViewController;
10 10
     RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
11 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 17
 - (void)applyOptions:(RNNNavigationOptions *)options {

+ 6
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj Voir le fichier

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

+ 16
- 0
lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m Voir le fichier

@@ -54,6 +54,22 @@
54 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 73
 - (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
58 74
     id uut = [self uut];
59 75
     [[uut expect] applyDotIndicator];

+ 9
- 1
lib/ios/ReactNativeNavigationTests/UITabBarController+RNNOptionsTest.m Voir le fichier

@@ -1,6 +1,7 @@
1 1
 #import <XCTest/XCTest.h>
2
-#import <OCMockObject.h>
2
+#import <OCMock/OCMock.h>
3 3
 #import "UITabBarController+RNNOptions.h"
4
+#import "UITabBar+utils.h"
4 5
 
5 6
 @interface UITabBarController_RNNOptionsTest : XCTestCase
6 7
 
@@ -13,6 +14,7 @@
13 14
 - (void)setUp {
14 15
     [super setUp];
15 16
 	self.uut = [OCMockObject partialMockForObject:[UITabBarController new]];
17
+	OCMStub([self.uut tabBar]).andReturn([OCMockObject partialMockForObject:[UITabBar new]]);
16 18
 }
17 19
 
18 20
 - (void)test_tabBarTranslucent_true {
@@ -39,6 +41,12 @@
39 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 50
 - (void)test_tabBarBackgroundColor {
43 51
 	UIColor* tabBarBackgroundColor = [UIColor redColor];
44 52
 

+ 7
- 0
lib/ios/UITabBar+utils.h Voir le fichier

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

+ 60
- 0
lib/ios/UITabBar+utils.m Voir le fichier

@@ -0,0 +1,60 @@
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 Voir le fichier

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

+ 5
- 0
lib/ios/UITabBarController+RNNOptions.m Voir le fichier

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