react-native icon indicating copy to clipboard operation
react-native copied to clipboard

Liquid Glass header shadows incorrectly invert when FlatList has inverted={true} on iOS 26

Open whidrubeld opened this issue 2 months ago • 12 comments

Description

Expected Behavior

When using inverted={true} FlatList, the header shadow/blur should maintain its normal appearance with proper content darkening underneath the transparent header.

Actual Behavior

Liquid Glass interface automatically detects the FlatList inversion and incorrectly inverts the header blur gradient. This causes:

  1. All visible content under the header becomes properly darkened (incorrect)
  2. All content that should be darkened under the header remains fully transparent (incorrect)

The shadow/blur gradient is effectively inverted, creating the opposite visual effect.

migrated from https://github.com/software-mansion/react-native-screens/issues/3293

Steps to reproduce

  1. Setting up headerTransparent: true if using Navigation OR add native elements to ViewController (Searchbar etc.)
  2. Add a FlatList with inverted={true} OR apply transform: [{ scaleY: -1 }]
  3. Scroll the list and observe the header shadow behavior
  4. Compare with a non-inverted FlatList

React Native Version

0.81.4

Affected Platforms

Runtime - iOS

Output of npx @react-native-community/cli info

System:
  OS: macOS 26.0.1
  CPU: (11) arm64 Apple M3 Pro
  Memory: 184.17 MB / 18.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.14.0
    path: /Users/vasgen/.nvm/versions/node/v22.14.0/bin/node
  Yarn:
    version: 3.6.1
    path: /Users/vasgen/.nvm/versions/node/v22.14.0/bin/yarn
  npm:
    version: 10.9.2
    path: /Users/vasgen/.nvm/versions/node/v22.14.0/bin/npm
  Watchman:
    version: 2025.03.03.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.16.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 25.0
      - iOS 26.0
      - macOS 26.0
      - tvOS 26.0
      - visionOS 26.0
      - watchOS 26.0
  Android SDK:
    API Levels:
      - "34"
    Build Tools:
      - 29.0.2
      - 32.0.0
      - 34.0.0
    System Images:
      - android-34 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.2 AI-242.23339.11.2421.12483815
  Xcode:
    version: 26.0.1/17A400
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.14
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 19.1.0
    wanted: 19.1.0
  react-native:
    installed: 0.81.4
    wanted: 0.81.4
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Stacktrace or Logs

-

MANDATORY Reproducer

https://snack.expo.dev/@whidrubeld/liquid-glass-header-shadows-incorrectly-invert-when-flatlist-has-inverted

Screenshots and Videos

Example of behavior Image Image

ViewController setting up Image

whidrubeld avatar Oct 16 '25 21:10 whidrubeld

I was able to reproduce this issue.

Thotaharshavardhani avatar Oct 17 '25 12:10 Thotaharshavardhani

Seeing this same issue. Is there any documented workaround at this point?

kbrandwijk avatar Nov 12 '25 16:11 kbrandwijk

I've been waiting for a solution to this too. https://github.com/expo/expo/issues/40409

vicovictor avatar Nov 14 '25 13:11 vicovictor

Seeing this same issue. Is there any documented workaround at this point?

Not that I've seen. My workaround is to set headerTransparent: false and then adjust padding accordingly. Either that or use a non-inverted list.

Between those two options having a temporary opaque header is the lesser evil. Having a non-inverted list is more of a hassle in my case.

Lundeful avatar Nov 14 '25 22:11 Lundeful

Seeing this same issue. Is there any documented workaround at this point?

dentified the following "workaround" solutions:

  • Disable transparent style for native elements;
  • Develop JS equivalents of the same elements;
  • Use LegendList as an equivalent.

In my scenarios, I quickly developed JS equivalents, since LegendList functionality turned out to be insufficient. Haven't studied the native layer issues yet.

whidrubeld avatar Nov 14 '25 23:11 whidrubeld

This is because UIScrollEdgeEffect introduced in iOS 26.

A temp workaround for new Arch is to apply this patch:

diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm
index c593d9e..161a804 100644
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm
@@ -41,6 +41,13 @@ - (instancetype)initWithFrame:(CGRect)frame
     // scrollbar flip because we also flip it with whole `UIScrollView` flip.
     self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
       
+    if (@available(iOS 26.0, *)) {
+      self.topEdgeEffect.hidden = YES;
+//      self.bottomEdgeEffect.hidden = YES;
+//      self.leftEdgeEffect.hidden = YES;
+//      self.rightEdgeEffect.hidden = YES;
+    }
+
     __weak __typeof(self) weakSelf = self;
     _delegateSplitter = [[RCTGenericDelegateSplitter alloc] initWithDelegateUpdateBlock:^(id delegate) {
       [weakSelf setPrivateDelegate:delegate];

Uncomment the edges you want to disable blur effect of. For this issue, an inverted vertical scroll view, it is the top edge. Be attention, this patch will affect all scrollview.

For old arch I didn't try, maybe you should add these lines to RCTScrollView.m

I'd like to open a PR to expose these four props, but I need to learn how to codegen from flowjs first.

Phecda avatar Nov 26 '25 08:11 Phecda

This is because UIScrollEdgeEffect introduced in iOS 26.

A temp workaround for new Arch is to apply this patch:

diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm index c593d9e..161a804 100644 --- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm +++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTEnhancedScrollView.mm @@ -41,6 +41,13 @@ - (instancetype)initWithFrame:(CGRect)frame // scrollbar flip because we also flip it with whole UIScrollView flip. self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;

  • if (@available(iOS 26.0, *)) {
  •  self.topEdgeEffect.hidden = YES;
    

+// self.bottomEdgeEffect.hidden = YES; +// self.leftEdgeEffect.hidden = YES; +// self.rightEdgeEffect.hidden = YES;

  • }
  • __weak __typeof(self) weakSelf = self; _delegateSplitter = [[RCTGenericDelegateSplitter alloc] initWithDelegateUpdateBlock:^(id delegate) { [weakSelf setPrivateDelegate:delegate]; Uncomment the edges you want to disable blur effect of. For this issue, an inverted vertical scroll view, it is the top edge. Be attention, this patch will affect all scrollview.

For old arch I didn't try, maybe you should add these lines to RCTScrollView.m

I'd like to open a PR to expose these four props, but I need to learn how to codegen from flowjs first.

Idkw, but it didn't work for me. I updated RCTScrollViewComponentView.mm instead to catch the inverted behavior, so other ScrollViews still keep the blur effect

diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
index 1494fd2..f333818 100644
--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
+++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
@@ -432,6 +432,15 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
   }
 
   [super updateProps:props oldProps:oldProps];
+    
+  [self disableEdgeEffectIos26OnInverted];
+}
+
+- (void) disableEdgeEffectIos26OnInverted
+{
+  if (@available(iOS 26.0, *)) {
+    _scrollView.topEdgeEffect.hidden = [self isInverted];
+  }
 }
 
 - (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState
@@ -762,6 +771,12 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
   RCTSendScrollEventForNativeAnimations_DEPRECATED(scrollView, self.tag, kOnScrollEndEvent);
 }
 
+- (void)layoutSubviews
+{
+  [super layoutSubviews];
+  [self disableEdgeEffectIos26OnInverted];
+}
+
 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
 {
   [self _handleFinishedScrolling:scrollView];
@@ -774,6 +789,8 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
   if (!self.window) {
     // The view is being removed, ensure that the scroll end event is dispatched
     [self _handleScrollEndIfNeeded];
+  } else {
+    [self disableEdgeEffectIos26OnInverted];
   }
 }

exodusanto avatar Nov 30 '25 20:11 exodusanto

Any updates on this issue? It's a gigantic blocker for me currently

ahmed-sudowrite avatar Dec 11 '25 15:12 ahmed-sudowrite

@ahmed-sudowrite check out my comment regarding this issue on another repo for a quick workaround:

https://github.com/FaridSafi/react-native-gifted-chat/issues/2665#issuecomment-3609149121

habovh avatar Dec 11 '25 15:12 habovh

@habovh yeah i've tried your solution, but for some reason I still have the same issue even with the transparent headers disabled. I'm not using the native headers at all so that might be why, i have headerShown: false everywhere, but it's worth noting that even when i do enable them and disable transparency the bug persists

ahmed-sudowrite avatar Dec 11 '25 15:12 ahmed-sudowrite

@ahmed-sudowrite sorry to hear, frankly I really hope this bug gets fixed soon for everybody.

habovh avatar Dec 11 '25 17:12 habovh

@whidrubeld @cipolleschi Hi! First of all, thank you for all your work on this project.

This is a huge blocker for my team currently- we're trying to ship a new version of our app with rn 0.83 this Friday Do you happen to have any estimates on when it might be fixed in a canary version of react native? Also, do you have any ideas for how it could be fixed? I'm eager to contribute a PR

ahmed-sudowrite avatar Dec 15 '25 15:12 ahmed-sudowrite