React-native-screens: LargeHeader stays small after pop/goBack/swipe gesture on iOS 14+

Created on 23 Sep 2020  路  38Comments  路  Source: software-mansion/react-native-screens

Hi,

I just upgraded to Expo SDK 39 and before of that, I had never an issue navigating to a child screen (from LargeHeader to small header). When I go back now, it stays smalls instead of growing back to large.

What has changed and is this something known?

bug iOS native-stack

Most helpful comment

I鈥檝e got the same problem on my Navigation router (@WoLewicki your fix didn鈥檛 work for me).

I鈥檝e worked out a fix but we need to tweak the RCTScrollContentView class in React Native. We must ensure that updateContentOffsetIfNeeded first runs after the ScrollView is moved to the window. So we prevent it running inside reactSetFrame if there鈥檚 no window yet and get it to run from didMoveToWindow instead.

Here鈥檚 the fixed RCTScrollContentView. Can you check it works for you, please? You can open your project in XCode and paste this code into RCTScrollContentView

@implementation RCTScrollContentView

- (void)didMoveToWindow
{
    [super didMoveToWindow];
    RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;
    [scrollView updateContentOffsetIfNeeded];
}

- (void)reactSetFrame:(CGRect)frame
{
  [super reactSetFrame:frame];

  RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;

  if (!scrollView || !self.window) {
    return;
  }

  RCTAssert([scrollView isKindOfClass:[RCTScrollView class]], @"Unexpected view hierarchy of RCTScrollView component.");

  [scrollView updateContentOffsetIfNeeded];
}

@end

All 38 comments

This looks like it has nothing to do with SDK 39, its an issue with iOS 14. It works on iOS 13.
Here is a video @WoLewicki https://streamable.com/x4vo01

The same code on iOS 13 will grow the large header back to its original size.

Yeah, I can spot problems with this. It looks like some kind of bug because it doesn't happen in the Settings of your phone. Am I right?

@WoLewicki you are right, works great everywhere else in the system.

The same here 馃憤

Does this behavior appear only while using ScrollView?

For me, it happens when using ScrollView and FlatList

Maybe this is related: iOS 14 Large title collapse when app run firstly.

I experience this issue too, when I start the app or scroll to the top, navigate and go back on iOS 14.

Can you check if #670 fixes the issue and does not introduce any new issues?

I鈥檝e got the same problem on my Navigation router (@WoLewicki your fix didn鈥檛 work for me).

I鈥檝e worked out a fix but we need to tweak the RCTScrollContentView class in React Native. We must ensure that updateContentOffsetIfNeeded first runs after the ScrollView is moved to the window. So we prevent it running inside reactSetFrame if there鈥檚 no window yet and get it to run from didMoveToWindow instead.

Here鈥檚 the fixed RCTScrollContentView. Can you check it works for you, please? You can open your project in XCode and paste this code into RCTScrollContentView

@implementation RCTScrollContentView

- (void)didMoveToWindow
{
    [super didMoveToWindow];
    RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;
    [scrollView updateContentOffsetIfNeeded];
}

- (void)reactSetFrame:(CGRect)frame
{
  [super reactSetFrame:frame];

  RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;

  if (!scrollView || !self.window) {
    return;
  }

  RCTAssert([scrollView isKindOfClass:[RCTScrollView class]], @"Unexpected view hierarchy of RCTScrollView component.");

  [scrollView updateContentOffsetIfNeeded];
}

@end

Looks like it works @grahammendick :tada:. Are you going to submit a PR with this in the react-native repository?

@WoLewicki I'm glad it works for you too!
I don't really like submitting PRs in the react-native repo. It takes me a while to set everything up to run locally and then they never look at the PR anyway. I'm happy for someone on this issue to submit the PR instead if they want

I agree with @grahammendick on this. If you're an unknown name in the original react-native repo, PRs won't get a merge in years. Super frustrating to submit something there. I don't know how many times I tried to get their (facebook) eyes on broken scroll-to-top behaviour on inverted scrollviews.

I think, you guys from SWM have a better reputation and are more likely to have success submitting something there.

Is there a chance to fix this trough react-native-screens instead of fixing it upstream? @WoLewicki

I tried fixing it upstream in my Navigation router but had no luck. The problem is we need to stop the updateContentOffsetIfNeeded running in reactSetFrame because that's too early. You can see what I mean by commenting out the !self.windowcheck in the fix and see it doesn't work anymore

@Hirbod You're right, but the react-native repo is just one example of the problems you have if you're an unknown name in React. I've found it impossible to get my work looked at. If you're not a name then you're not getting in

@WoLewicki can someone from your team create PR to react-native repository?

It would need more debugging, submitting a PR with changes to a component used by almost every developer must have a very strong explanation and must ensure that no right behavior will be broken after it.

For me, this quick hack worked.

return(

      <FlatList
           contentOffset={{ x: 0, y: -1}}
                contentInsetAdjustmentBehavior={"always"}
                maintainVisibleContentPosition={
                    {

                        autoscrollToTopThreshold: -1,
                        minIndexForVisible: -1
                    }
                }
       > 
          {/*your content goes here */}
       </FlatList>
)

@DasArthur in my test cases, it looks like there is some race condition and it sometimes loads initially a large header and sometimes a small one. Can you check this solution @ArekChr @grahammendick ?

I remember trying a variety of contentOffset and contentInset and repeatedly reloading - sometimes it worked and sometimes it didn't. My guess is that the times it worked is when the reactSetFrame was called after it was added to the window - @DasArthur Could you check if the timing of the reactSetFrame changes when it works and when it doesn't? Or does it always work for you every time you reload?

I don't have maintainVisibleContentPosition prop, @DasArthur what version of RN are you using?

The workaround of @alexanderstrom seems to be the only reliable one. This is where you conditionally render the ScrollView after a delay (useEffect with a timeout of 0). I think it works for the same reason that my fix works - the delayed render means that didMoveToWindow runs before reactSetFrame. It runs as soon as it鈥檚 added as a subview because the parent is already in the window.

@grahammendick still feels so hacky :/

@Hirbod I think another option is you could use patch-package to apply the fix to RCTScrollContentView

@grahammendick unfortunately not an option for me, because I'm still on Expo and I don't want to eject. Since I only have 3 LargeTitle Screens, I'll try the delay render useEffect hack.

@Hirbod You can see that I'm using it in the zoom example app in my Navigation router

Weird, for me it was simply enough to add this:
My LargeHeader works as expected again. Also not nice, but better then the effect hack imho

        <>
            <View></View>
            <FlatList>...
        </>

So just empty brackets surrounding and an empty view before my Scrollview.

Ohhh... ok, my hack "works" for the back button, but it won't collapse when I scroll now. Dang it!

None of those workarounds working for me, even delaying the Scrollview render @grahammendick. I don't have any INITIAL issues with the LargeHeader, it is working for me always, its just when navigating to a stack-screen and returning. The header stays small for me after I pop. Only my mentioned "workaround" fixes this, but will break collapsing on scrolling :/

@Hirbod Ok, my fix probably doesn't work for you either, then. Can you temporarily eject from expo, apply the fix and let me know if it works, please?

@grahammendick I tested it on iOS so far and it seems to work well! Thank you.

Here the patch react-native+0.63.3.patch for patch-package:

diff --git a/node_modules/react-native/React/Views/ScrollView/RCTScrollContentView.m b/node_modules/react-native/React/Views/ScrollView/RCTScrollContentView.m
index cf6a0b1..4d187db 100644
--- a/node_modules/react-native/React/Views/ScrollView/RCTScrollContentView.m
+++ b/node_modules/react-native/React/Views/ScrollView/RCTScrollContentView.m
@@ -14,13 +14,20 @@

 @implementation RCTScrollContentView

+- (void)didMoveToWindow
+{
+    [super didMoveToWindow];
+    RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;
+    [scrollView updateContentOffsetIfNeeded];
+}
+
 - (void)reactSetFrame:(CGRect)frame
 {
   [super reactSetFrame:frame];

   RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;

-  if (!scrollView) {
+  if (!scrollView || !self.window) {
     return;
   }

I used the useIsFocused() hook from react navigation

For doing what?

Is there any update on this for expo users?

same issus from expo users

Just for record I applied the @grahammendick patch, it works for initial rendering, but not for back

@grahammendick I can also confirm that I've made a temp eject and your applied patch did not fix the grow to large after a "pop / back". Still stays small.

@Hirbod @fdecampredon I applied the patch to the zoom sample in my Navigation router and it works for both initial rendering and after a pop/back. Give the sample a try and it might give you a clue to what's different about React Navigation?

@pouyarezvani you have to use patch-package library for this

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thomasgosse picture thomasgosse  路  4Comments

thorbenandresen picture thorbenandresen  路  4Comments

bartzy picture bartzy  路  3Comments

iDuuck picture iDuuck  路  5Comments

jeveloper picture jeveloper  路  5Comments