Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ios/RNNBottomTabsController.mm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "RNNBottomTabsController.h"
#import "UITabBarController+RNNOptions.h"
#import "UITabBarController+RNNUtils.h"

@interface RNNBottomTabsController ()
Expand Down Expand Up @@ -100,6 +101,8 @@ - (void)createTabBarItems:(NSArray<UIViewController *> *)childViewControllers {
for (UIViewController *child in childViewControllers) {
[_bottomTabPresenter applyOptions:child.resolveOptions child:child];
}

[self syncTabBarItemTestIDs];
}

- (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child {
Expand All @@ -111,6 +114,8 @@ - (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewControlle
[_dotIndicatorPresenter mergeOptions:options
resolvedOptions:childViewController.resolveOptions
child:childViewController];

[self syncTabBarItemTestIDs];
}

- (id<UITabBarControllerDelegate>)delegate {
Expand All @@ -123,6 +128,7 @@ - (void)render {

- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self syncTabBarItemTestIDs];
[self.presenter viewDidLayoutSubviews];
[_dotIndicatorPresenter bottomTabsDidLayoutSubviews:self];
}
Expand Down
2 changes: 2 additions & 0 deletions ios/UITabBarController+RNNOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@

- (void)hideTabBar:(BOOL)animated;

- (void)syncTabBarItemTestIDs;

@end
108 changes: 108 additions & 0 deletions ios/UITabBarController+RNNOptions.mm
Original file line number Diff line number Diff line change
@@ -1,9 +1,85 @@
#import "RNNBottomTabsController.h"
#import "UITabBar+utils.h"
#import "UITabBarController+RNNOptions.h"
#import <objc/runtime.h>

static const NSTimeInterval RNNTabBarTestIDRetryDelay = 0.15;
static const NSUInteger RNNTabBarTestIDMaxRetryAttempts = 5;
static const void *RNNTabBarTestIDRetryScheduledKey = &RNNTabBarTestIDRetryScheduledKey;
static const void *RNNTabBarTestIDRetryAttemptsKey = &RNNTabBarTestIDRetryAttemptsKey;
static const void *RNNOriginalTabBarViewAccessibilityIdentifierKey =
&RNNOriginalTabBarViewAccessibilityIdentifierKey;

@implementation UITabBarController (RNNOptions)

- (void)rnn_storeOriginalAccessibilityIdentifierIfNeededForTabView:(UIView *)tabView {
if (objc_getAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey))
return;

id originalAccessibilityIdentifier = tabView.accessibilityIdentifier ?: [NSNull null];
objc_setAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey,
originalAccessibilityIdentifier,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)rnn_applyTestID:(NSString *)testID toTabView:(UIView *)tabView {
[self rnn_storeOriginalAccessibilityIdentifierIfNeededForTabView:tabView];
tabView.accessibilityIdentifier = testID;
}

- (void)rnn_restoreOriginalAccessibilityIdentifierForTabView:(UIView *)tabView {
id originalAccessibilityIdentifier =
objc_getAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey);
if (!originalAccessibilityIdentifier)
return;

tabView.accessibilityIdentifier =
[originalAccessibilityIdentifier isKindOfClass:NSNull.class]
? nil
: originalAccessibilityIdentifier;
objc_setAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey, nil,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)rnn_isTabBarTestIDRetryScheduled {
return [objc_getAssociatedObject(self, RNNTabBarTestIDRetryScheduledKey) boolValue];
}

- (void)rnn_setTabBarTestIDRetryScheduled:(BOOL)scheduled {
objc_setAssociatedObject(self, RNNTabBarTestIDRetryScheduledKey, @(scheduled),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSUInteger)rnn_tabBarTestIDRetryAttempts {
NSNumber *retryAttempts = objc_getAssociatedObject(self, RNNTabBarTestIDRetryAttemptsKey);
return retryAttempts.unsignedIntegerValue;
}

- (void)rnn_setTabBarTestIDRetryAttempts:(NSUInteger)retryAttempts {
objc_setAssociatedObject(self, RNNTabBarTestIDRetryAttemptsKey, @(retryAttempts),
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)rnn_applyTabBarItemTestIDs {
NSArray<UITabBarItem *> *items = self.tabBar.items ?: @[];
BOOL appliedAllKnownTestIDs = YES;

for (NSUInteger tabIndex = 0; tabIndex < items.count; tabIndex++) {
UITabBarItem *item = items[tabIndex];
NSString *testID = item.accessibilityIdentifier;
UIView *tabView = [self.tabBar tabBarItemViewAtIndex:tabIndex];
if (testID.length > 0 && tabView) {
[self rnn_applyTestID:testID toTabView:tabView];
} else if (tabView) {
[self rnn_restoreOriginalAccessibilityIdentifierForTabView:tabView];
} else if (testID.length > 0) {
appliedAllKnownTestIDs = NO;
}
}

return appliedAllKnownTestIDs;
}

- (void)setCurrentTabIndex:(NSUInteger)currentTabIndex {
[self setSelectedIndex:currentTabIndex];
}
Expand All @@ -16,6 +92,38 @@ - (void)setTabBarTestID:(NSString *)testID {
self.tabBar.accessibilityIdentifier = testID;
}

- (void)syncTabBarItemTestIDs {
if ([self rnn_applyTabBarItemTestIDs]) {
[self rnn_setTabBarTestIDRetryScheduled:NO];
[self rnn_setTabBarTestIDRetryAttempts:0];
return;
}

if ([self rnn_isTabBarTestIDRetryScheduled])
return;

NSUInteger retryAttempts = [self rnn_tabBarTestIDRetryAttempts];
if (retryAttempts >= RNNTabBarTestIDMaxRetryAttempts) {
[self rnn_setTabBarTestIDRetryAttempts:0];
return;
}

[self rnn_setTabBarTestIDRetryScheduled:YES];
[self rnn_setTabBarTestIDRetryAttempts:retryAttempts + 1];

__weak UITabBarController *weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(RNNTabBarTestIDRetryDelay * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
UITabBarController *controller = weakSelf;
if (!controller)
return;

[controller rnn_setTabBarTestIDRetryScheduled:NO];
[controller syncTabBarItemTestIDs];
});
}

- (void)setTabBarStyle:(UIBarStyle)barStyle {
self.tabBar.barStyle = barStyle;
}
Expand Down
Loading