Skip to content

Commit 0e7375a

Browse files
Douglas Lowderfacebook-github-bot
authored andcommitted
Apple TV: RCTTabBar selection controlled by native after render (fix facebook#15081)
Summary: **Motivation** Fix flickering in TabBarIOS on Apple TV... issue facebook#15081 After this change, on Apple TV, TabBarIOS item selections will be controlled purely from the native side after initial render with the `selected` prop. This is necessary because the `UITabBar` implementation in tvOS moves the selection before calling `shouldSelectViewController:`; this issue does not occur on iOS. **Test plan** Existing CI should still pass. Issue is resolved when testing the example code in facebook#15081 . Closes facebook#15220 Differential Revision: D5601671 Pulled By: javache fbshipit-source-id: c18e7d3482d6c07d534ff40a443a6f642d4267bb
1 parent 3a031cc commit 0e7375a

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

React/Views/RCTTabBar.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,17 @@ - (void)reactBridgeDidFinishTransaction
115115
[tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self.tintColor} forState:UIControlStateSelected];
116116

117117
controller.tabBarItem = tab.barItem;
118+
#if TARGET_OS_TV
119+
// On Apple TV, disable JS control of selection after initial render
120+
if (tab.selected && !tab.wasSelectedInJS) {
121+
self->_tabController.selectedViewController = controller;
122+
}
123+
tab.wasSelectedInJS = YES;
124+
#else
118125
if (tab.selected) {
119126
self->_tabController.selectedViewController = controller;
120127
}
128+
#endif
121129
}];
122130
}
123131

@@ -175,6 +183,18 @@ - (void)setItemPositioning:(UITabBarItemPositioning)itemPositioning
175183

176184
#pragma mark - UITabBarControllerDelegate
177185

186+
#if TARGET_OS_TV
187+
188+
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(nonnull UIViewController *)viewController
189+
{
190+
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
191+
RCTTabBarItem *tab = (RCTTabBarItem *)self.reactSubviews[index];
192+
if (tab.onPress) tab.onPress(nil);
193+
return;
194+
}
195+
196+
#else
197+
178198
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
179199
{
180200
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
@@ -183,6 +203,8 @@ - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectView
183203
return NO;
184204
}
185205

206+
#endif
207+
186208
#if TARGET_OS_TV
187209

188210
- (BOOL)isUserInteractionEnabled

React/Views/RCTTabBarItem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@
2929
@property (nonatomic, readonly) UITabBarItem *barItem;
3030
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
3131

32+
#if TARGET_OS_TV
33+
@property (nonatomic, assign) BOOL wasSelectedInJS;
34+
#endif
35+
3236
@end

React/Views/RCTTabBarItem.m

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ - (instancetype)initWithFrame:(CGRect)frame
4242
{
4343
if ((self = [super initWithFrame:frame])) {
4444
_systemIcon = NSNotFound;
45+
#if TARGET_OS_TV
46+
_wasSelectedInJS = NO;
47+
#endif
4548
}
4649
return self;
4750
}
@@ -118,4 +121,16 @@ - (UIViewController *)reactViewController
118121
return self.superview.reactViewController;
119122
}
120123

124+
#if TARGET_OS_TV
125+
126+
// On Apple TV, we let native control the tab bar selection after initial render
127+
- (void)setSelected:(BOOL)selected
128+
{
129+
if (!_wasSelectedInJS) {
130+
_selected = selected;
131+
}
132+
}
133+
134+
#endif
135+
121136
@end

docs/BuildingForAppleTV.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class Game2048 extends React.Component {
8989
9090
- *Back navigation with the TV remote menu button*: The `BackHandler` component, originally written to support the Android back button, now also supports back navigation on the Apple TV using the menu button on the TV remote.
9191
92+
- *TabBarIOS behavior*: The `TabBarIOS` component wraps the native `UITabBar` API, which works differently on Apple TV. To avoid jittery rerendering of the tab bar in tvOS (see [this issue](https://github.com/facebook/react-native/issues/15081)), the selected tab bar item can only be set from Javascript on initial render, and is controlled after that by the user through native code.
93+
9294
- *Known issues*:
9395
9496
- [ListView scrolling](https://github.com/facebook/react-native/issues/12793). The issue can be easily worked around by setting `removeClippedSubviews` to false in ListView and similar components. For more discussion of this issue, see [this PR](https://github.com/facebook/react-native/pull/12944).

0 commit comments

Comments
 (0)