Skip to content

Commit

Permalink
Support centered bottomTab icons using bottomTabs.titleDisplayMode op…
Browse files Browse the repository at this point in the history
…tion (#5667)

* Support centered bottomTab icons using bottomTabs.titleDisplayMode option

* Improve readability, better namings

* Rename setTabItemImagesCentered to centerTabItems
  • Loading branch information
yogevbd authored Nov 24, 2019
1 parent 619af3e commit 26d3d82
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/ios/RNNBottomTabsOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
@property (nonatomic, strong) Text* currentTabId;
@property (nonatomic, strong) Text* barStyle;
@property (nonatomic, strong) Text* fontFamily;
@property (nonatomic, strong) Text* titleDisplayMode;

@end
1 change: 1 addition & 0 deletions lib/ios/RNNBottomTabsOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ - (instancetype)initWithDict:(NSDictionary *)dict {
self.currentTabId = [TextParser parse:dict key:@"currentTabId"];
self.barStyle = [TextParser parse:dict key:@"barStyle"];
self.fontFamily = [TextParser parse:dict key:@"fontFamily"];
self.titleDisplayMode = [TextParser parse:dict key:@"titleDisplayMode"];

return self;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/ios/RNNBottomTabsPresenter.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ - (void)applyOptionsOnInit:(RNNNavigationOptions *)options {
UITabBarController *bottomTabs = self.boundViewController;
RNNNavigationOptions *withDefault = [options withDefault:[self defaultOptions]];
[bottomTabs setCurrentTabIndex:[withDefault.bottomTabs.currentTabIndex getWithDefaultValue:0]];
if ([[withDefault.bottomTabs.titleDisplayMode getWithDefaultValue:@"alwaysShow"] isEqualToString:@"alwaysHide"]) {
[bottomTabs centerTabItems];
}
}

- (void)applyOptions:(RNNNavigationOptions *)options {
Expand Down
6 changes: 6 additions & 0 deletions lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
4534E72520CB6724009F8185 /* RNNLargeTitleOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */; };
4534E72620CB6724009F8185 /* RNNLargeTitleOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */; };
5008641223856A2D00A55BE9 /* UITabBar+utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5008641023856A2C00A55BE9 /* UITabBar+utils.m */; };
501214C9217741A000435148 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 501214C8217741A000435148 /* libOCMock.a */; };
501223D72173590F000F5F98 /* RNNStackPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 501223D52173590F000F5F98 /* RNNStackPresenter.h */; };
501223D82173590F000F5F98 /* RNNStackPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 501223D62173590F000F5F98 /* RNNStackPresenter.m */; };
Expand Down Expand Up @@ -399,6 +400,8 @@
390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
4534E72320CB6724009F8185 /* RNNLargeTitleOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNLargeTitleOptions.h; sourceTree = "<group>"; };
4534E72420CB6724009F8185 /* RNNLargeTitleOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNLargeTitleOptions.m; sourceTree = "<group>"; };
5008641023856A2C00A55BE9 /* UITabBar+utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UITabBar+utils.m"; sourceTree = "<group>"; };
5008641123856A2D00A55BE9 /* UITabBar+utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UITabBar+utils.h"; sourceTree = "<group>"; };
501214C8217741A000435148 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = "<group>"; };
501223D52173590F000F5F98 /* RNNStackPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNStackPresenter.h; sourceTree = "<group>"; };
501223D62173590F000F5F98 /* RNNStackPresenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNStackPresenter.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -733,6 +736,8 @@
50706E6C20CE7CA5003345C3 /* UIImage+tint.m */,
506317A8220B547400B26FC3 /* UIImage+insets.h */,
506317A9220B547400B26FC3 /* UIImage+insets.m */,
5008641123856A2D00A55BE9 /* UITabBar+utils.h */,
5008641023856A2C00A55BE9 /* UITabBar+utils.m */,
5038A372216CDDB6009280BC /* UIViewController+SideMenuController.h */,
5038A373216CDDB6009280BC /* UIViewController+SideMenuController.m */,
5038A3BF216E1E66009280BC /* RNNFontAttributesCreator.h */,
Expand Down Expand Up @@ -1604,6 +1609,7 @@
501224072173592D000F5F98 /* RNNBottomTabsPresenter.m in Sources */,
50A00C38200F84D6000F01A6 /* RNNOverlayOptions.m in Sources */,
5039559C2174867000B0A663 /* DoubleParser.m in Sources */,
5008641223856A2D00A55BE9 /* UITabBar+utils.m in Sources */,
E5F6C3A822DB4D0F0093C2CE /* UIView+Utils.m in Sources */,
5049593F216F5D73006D2B81 /* BoolParser.m in Sources */,
50EB93421FE14A3E00BD8EEE /* RNNBottomTabOptions.m in Sources */,
Expand Down
16 changes: 16 additions & 0 deletions lib/ios/ReactNativeNavigationTests/RNNTabBarPresenterTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ - (void)testApplyOptions_shouldApplyOptions {
[self.boundViewController verify];
}

- (void)testApplyOptionsOnInit_alwaysShow_shouldNotCenterTabImages {
RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysShow"];
[[self.boundViewController reject] centerTabItems];
[self.uut applyOptionsOnInit:initialOptions];
[self.boundViewController verify];
}

- (void)testApplyOptions_shouldApplyOptionsOnInit_alwaysHide_shouldCenterTabImages {
RNNNavigationOptions *initialOptions = [[RNNNavigationOptions alloc] initEmptyOptions];
initialOptions.bottomTabs.titleDisplayMode = [[Text alloc] initWithValue:@"alwaysHide"];
[[self.boundViewController expect] centerTabItems];
[self.uut applyOptionsOnInit:initialOptions];
[self.boundViewController verify];
}

- (void)testViewDidLayoutSubviews_appliesBadgeOnNextRunLoop {
id uut = [self uut];
[[uut expect] applyDotIndicator];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import <XCTest/XCTest.h>
#import <OCMockObject.h>
#import <OCMock/OCMock.h>
#import "UITabBarController+RNNOptions.h"
#import "UITabBar+utils.h"

@interface UITabBarController_RNNOptionsTest : XCTestCase

Expand All @@ -13,6 +14,7 @@ @implementation UITabBarController_RNNOptionsTest
- (void)setUp {
[super setUp];
self.uut = [OCMockObject partialMockForObject:[UITabBarController new]];
OCMStub([self.uut tabBar]).andReturn([OCMockObject partialMockForObject:[UITabBar new]]);
}

- (void)test_tabBarTranslucent_true {
Expand All @@ -39,6 +41,12 @@ - (void)test_tabBarHideShadow_false {
XCTAssertFalse(self.uut.tabBar.clipsToBounds);
}

- (void)test_centerTabItems {
[[(id)self.uut.tabBar expect] centerTabItems];
[self.uut centerTabItems];
[(id)self.uut.tabBar verify];
}

- (void)test_tabBarBackgroundColor {
UIColor* tabBarBackgroundColor = [UIColor redColor];

Expand Down
7 changes: 7 additions & 0 deletions lib/ios/UITabBar+utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>

@interface UITabBar (utils)

- (void)centerTabItems;

@end
60 changes: 60 additions & 0 deletions lib/ios/UITabBar+utils.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#import "UITabBar+utils.h"
#import <objc/runtime.h>

#define BADGE_OFFSET 0.2
#define IMAGE_VIEW_TAG 1

typedef void (*UITabBarButton_layoutSubviews__IMP)(void);
static UITabBarButton_layoutSubviews__IMP original_UITabBarButton_layoutSubviews;

@implementation UITabBar (utils)

- (void)centerTabItems {
[self removeTabBarItemTitles];
[self swizzleUITabBarButton];
}

- (void)removeTabBarItemTitles {
for (UITabBarItem* item in self.items) {
item.title = nil;
}
}

- (void)swizzleUITabBarButton {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[self class] swizzleUITabBarButtonLayoutSubviews];
});
}

+ (void)swizzleUITabBarButtonLayoutSubviews {
Class UITabBarButtonClass = NSClassFromString(@"UITabBarButton");

SEL layoutSubviewsSEL = @selector(layoutSubviews);
Method layoutSubviewsMethod = class_getInstanceMethod(UITabBarButtonClass, layoutSubviewsSEL);
IMP layoutSubviewsIMP = method_getImplementation(layoutSubviewsMethod);

original_UITabBarButton_layoutSubviews = layoutSubviewsIMP;

SEL swizzleUITabBarButton_layoutSubviewsSEL = @selector(swizzleUITabBarButton_layoutSubviews);
Method swizzleUITabBarButton_layoutSubviewsMethod = class_getInstanceMethod(self, swizzleUITabBarButton_layoutSubviewsSEL);

method_exchangeImplementations(layoutSubviewsMethod, swizzleUITabBarButton_layoutSubviewsMethod);
}

- (void)swizzleUITabBarButton_layoutSubviews {
original_UITabBarButton_layoutSubviews();
for (UIView *subView in self.subviews) {
if ([subView isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")]) {
subView.center = CGPointMake(subView.center.x, subView.superview.frame.size.height / 2);
subView.tag = IMAGE_VIEW_TAG;
}

if ([subView isKindOfClass:NSClassFromString(@"_UIBadgeView")]) {
UIView* imageView = [subView.superview viewWithTag:IMAGE_VIEW_TAG];
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);
}
}
}

@end
2 changes: 2 additions & 0 deletions lib/ios/UITabBarController+RNNOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@

- (void)setTabBarVisible:(BOOL)visible animated:(BOOL)animated;

- (void)centerTabItems;

@end
5 changes: 5 additions & 0 deletions lib/ios/UITabBarController+RNNOptions.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#import "UITabBarController+RNNOptions.h"
#import "RNNBottomTabsController.h"
#import "UITabBar+utils.h"

@implementation UITabBarController (RNNOptions)

Expand Down Expand Up @@ -74,6 +75,10 @@ - (void)setTabBarVisible:(BOOL)visible animated:(BOOL)animated {
}
}

- (void)centerTabItems {
[self.tabBar centerTabItems];
}

- (void)forEachTab:(void (^)(UIView *, UIViewController * tabViewController, int tabIndex))performOnTab {
int tabIndex = 0;
for (UIView * tab in self.tabBar.subviews) {
Expand Down

1 comment on commit 26d3d82

@minhtc
Copy link
Contributor

@minhtc minhtc commented on 26d3d82 Dec 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crashed on iPhone5 (ios 10):

UITabBar+utils.m - Line 47
-[UITabBar(utils) swizzleUITabBarButton_layoutSubviews] + 47

Please sign in to comment.