Skip to content

Commit c789812

Browse files
authored
fix(iOS, Tabs): overrideScrollViewContentInsetAdjustmentBehavior in managed tabs (#3441)
## Description Fixes `overrideScrollViewContentInsetAdjustmentBehavior` in managed tabs on iOS, Fabric. | before | after | | --- | --- | | <video src="https://github.com/user-attachments/assets/c3ba2519-c710-4348-836c-b06d5ccb2c2e" /> | <video src="https://github.com/user-attachments/assets/d593dfcc-e758-406b-8a1c-d8ab89ac1303" /> | ### Details Previously, `overrideScrollViewBehaviorInFirstDescendantChainIfNeeded` was called in `updateSelectedViewController` which is called when `reactMountingTransactionDidMount`. This worked without any problems with controlled tabs as tab changes happen only via `updateSelectedViewController` in reaction to update from JS (the native tab change is prevented by returning `NO` from `tabBarController:shouldSelectViewController:` and appropriate event is sent to JS). In managed tabs, native tab change is not prevented and screen view controller is selected immediately. Tab screen is temporarily empty before content is mounted. When the content is mounted, it immediately becomes visible. After transaction had been mounted, we would run `overrideScrollViewBehaviorInFirstDescendantChainIfNeeded` but this was too late - screen's content was already visible so overriding behavior wouldn't change initial scroll position. To provide a solution that will work in both control modes, we decided to run `overrideScrollViewBehaviorInFirstDescendantChainIfNeeded` exactly when child is mounted to tab screen. This works on Fabric because children are mounted from bottom to top - when child is mounted to tab screen, we're sure that the entire hierarchy below it already exists. ### Paper On Paper, due to the order of mounting children, we are unable to consistently find and override scroll view behavior in time (when the entire hierarchy is mounted under screen but before screen is shown). The best solution I found (`dispatch_async` in `didUpdateReactSubviews`) worked 50% of the time. For now, we decided not to implement support for the prop on Paper. Ticket to research Paper support: software-mansion/react-native-screens-labs#283. ## Changes - move finding ScrollView and applying behavior override + scroll edge effects to `mountChildComponentView` ## Test code and steps to reproduce Run `TestBottomTabs`. Change Tab4 to use transparent header on first screen. Open Tab3, Tab4. ScrollViews should respect safe area insets. Use controlled and managed bottom tabs, with and without freeze. ## Checklist - [x] Included code example that can be used to test this change - [x] Updated documentation - [x] Ensured that CI passes
1 parent 1999a2b commit c789812

File tree

4 files changed

+10
-5
lines changed

4 files changed

+10
-5
lines changed

ios/bottom-tabs/host/RNSTabBarController.mm

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ - (void)updateSelectedViewController
162162
} else {
163163
self.tabBar.overrideUserInterfaceStyle = selectedViewController.tabScreenComponentView.userInterfaceStyle;
164164
}
165-
[selectedViewController.tabScreenComponentView overrideScrollViewBehaviorInFirstDescendantChainIfNeeded];
166-
[selectedViewController.tabScreenComponentView updateContentScrollViewEdgeEffectsIfExists];
165+
167166
[self setSelectedViewController:selectedViewController];
168167
}
169168

ios/bottom-tabs/screen/RNSBottomTabsScreenComponentView.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,13 @@ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompone
456456
{
457457
RNSLog(@"TabScreen [%ld] mount [%ld] at %ld", self.tag, childComponentView.tag, index);
458458
[super mountChildComponentView:childComponentView index:index];
459+
460+
// overrideScrollViewBehavior and updateContentScrollViewEdgeEffects use first descendant chain
461+
// from screen to find ScrollView, that's why we're only interested in child mounted at index 0.
462+
if (index == 0) {
463+
[self overrideScrollViewBehaviorInFirstDescendantChainIfNeeded];
464+
[self updateContentScrollViewEdgeEffectsIfExists];
465+
}
459466
}
460467

461468
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index

src/components/bottom-tabs/BottomTabs.types.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,6 @@ export interface BottomTabsProps extends ViewProps {
265265
* If set to false, tab screen change will not be prevented by the
266266
* native side (managed/natively-driven).
267267
*
268-
* On iOS, some features are not fully implemented for managed tabs
269-
* (e.g. overrideScrollViewContentInsetAdjustmentBehavior).
270-
*
271268
* On Android, only controlled tabs are currently supported and the
272269
* value of this prop is ignored.
273270
*

src/components/bottom-tabs/BottomTabsScreen.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,8 @@ export interface BottomTabsScreenProps {
504504
* prevents ScrollViews from respecting navigation bar insets.
505505
* When this prop is set to `true`, `automatic` behavior is reverted.
506506
*
507+
* Supported only on Fabric.
508+
*
507509
* @default true
508510
*
509511
* @platform ios

0 commit comments

Comments
 (0)