software-mansion / software-mansion/react-native-screens

[iOS 26] tabBarMinimizeBehavior never engages when tab content is a nested stack / virtualize list

Open
#4,145 11 comments 3 reactions 1 assignee View on GitHub

@kkafar is already working on this.

Since Jun 10, 2026.

platform:ios
Dominant language
TypeScript
Stars
3.7k
Forks
713
Avg merge
2d 23h
Merged PRs (30d)
71

Description

Description

On iOS 26, tabBarMinimizeBehavior: 'onScrollDown' never engages when the tab's content is a nested (native stack) navigator and/or the scrolling content is a virtualized list (FlashList, LegendList) or any scroll view behind styled wrapper views — i.e. the common shape of a production app. The tab bar never minimizes, and a bottom accessory never transitions to its inline placement.

#3954 covered a narrow variant of this (scroll view wrapped in a single styled View) and was closed with the collapsable={false} workaround. That workaround can't help here: with a nested stack the first-descendant chain from the TabsScreen runs through the stack host and screen views, and with virtualized lists + headers the UIScrollView is simply not on the first-subview chain at all — there is nothing the app author can collapsable={false} their way out of.

Mechanism

UIKit resolves the scroll view that drives the minimize behavior by asking the selected tab's view controller for its bottom-edge content scroll view (contentScrollView(for:) / automatic detection). UIKit's automatic detection cannot find a UIScrollView nested inside a React Native screen tree, so it tracks nothing and UITabBarController.tabBarMinimizeBehavior is inert.

Reproduction shape
NativeTabs (tabBarMinimizeBehavior: onScrollDown)
└─ Tab
   └─ native stack navigator
      └─ screen
         └─ styled wrappers / headers
            └─ FlashList / LegendList / ScrollView

RNS 4.25.2, RN 0.85, iOS 26, new arch. (Same report exists for react-native-bottom-tabs: callstack/react-native-bottom-tabs#496, where swizzling contentScrollView(for:) is the known fix.)

Proposed fix (running in production via a patch)

Implement contentScrollViewForEdge: — UIKit's documented override point for exactly this — on RNSTabsScreenViewController and RNSScreen, falling back to a breadth-first search when UIKit's own resolution returns nil. Restricted to the bottom edge so top-edge/navigation-bar behaviors are untouched:

- (UIScrollView *)contentScrollViewForEdge:(NSDirectionalRectEdge)edge
{
  UIScrollView *scrollView = [super contentScrollViewForEdge:edge];
  if (scrollView != nil) {
    return scrollView;
  }
  if (edge == NSDirectionalRectEdgeBottom) {
    return [RNSScrollViewFinder findScrollViewBreadthFirstFrom:self.view];
  }
  return nil;
}

plus a new finder on RNSScrollViewFinder (the existing first-descendant-chain walk is what fails on real screens):

// Bounded BFS; skips horizontal-only scrollers (carousels) so the tab bar
// tracks vertical scrolling. ~2000-view visit cap as a safety bound.
+ (nullable UIScrollView *)findScrollViewBreadthFirstFrom:(nullable UIView *)view;

With both overrides in place (covering whichever VC UIKit queries — tab root or the visible nested-stack screen), minimize + inline accessory work with nested stacks and LegendList/FlashList content, verified on a physical iOS 26 device.

Notes / open questions for maintainers:

  • The RNSScreen override applies to all stack screens, not only those inside tabs. We saw no regressions (bottom-edge only), but you may prefer gating it to tab-hosted screens or behind a prop.
  • An alternative shape would be extending the RNSContentScrollViewProviding delegation so the BFS lives behind the existing provider protocol.

Happy to open a PR with this.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.