React-native-navigation: White flash on screen push.

Created on 5 Oct 2016  Â·  101Comments  Â·  Source: wix/react-native-navigation

Issue Description

In IOS when i push a new screen i see a white background on the rootview before the first react native render, since i have a dark styled app it's quite annoying.

Changing the color on rootview in AppDelegate.m to black,
or setting a screenBackgroundColor doesn't seem to to anything

Steps to Reproduce / Code Snippets / Screenshots

This is the snippet from AppDeletage.m
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor blackColor];

This is the snipper from the screenComponent

class EventScreen extends Component { static navigatorStyle = { screenBackgroundColor: SituaColors.darkBg }; ....

Thanks

Environment

  • React Native Navigation version: 2.0.0-experimental.105
  • React Native version: 0.34.0
  • Platform: IOS
  • Device info: Both simulator and device, both debug and release
iOS v2

Most helpful comment

@ryanscottaudio this can be solved by a secret not documented on push option:

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

It may cause delays in pushing new screen on slower devices though

All 101 comments

Would be great to get some options for this. I also have a dark themed app and the white flashes between scenes are frustrating.

same issue here

I add this as temp solution for my project under setStyleOnAppearForViewController in RCCViewController

NSString *screenColor = self.navigatorStyle[@"screenColor"];
if (screenColor)
{
  UIColor *color = screenColor != (id)[NSNull null] ? [RCTConvert UIColor:screenColor] : nil;
  viewController.view.backgroundColor = color;
}

Maybe we create smth more nice, like screenStyles prop?

@savelichalex thanks! works like a charm!

Thanks @savelichalex it works well but does somebody a way to avoid this flashes ? maybe with preloading or idk ?

Maybe @guyca help with it, but I don't think so. As I understand how it works, when push happened VC create new native view(1), then they send event to js that render react view, and then rendered new react view added to native view(2). Between this two phases we see default native view with default background color.

Hey guys this issue will be addressed soon, I hope by the end of November. Sorry for the inconvenience.

@savelichalex you're correct, that's exactly what happens. On Android we avoid this issue by showing the pushed screen only after render is done and the native view is populated.

Hello, do you have an idea when pre-rendering will be available on iOS so we don't see anymore this white screen when pushing view or showing modal? Or maybe you can point us what should be changed so we can try to work on it? Thanks!

This is a big problem when switching tabs for the first time.
Thinking about migrating to a javascript-based tab navigator.

@brunolemos yeah, I've been using NavigationExperimental for now

@brunolemos @ryanscottaudio @pascalmerme see #437

I am experiencing the same issue when using switchToTab. Tab's screen content flashes when it is switched for the first time. It is even worse for me because I am using a custom (JS) tabbar and it flashes too.

@shahen94 This doesn't fix the flash issue for me on iOS. Even if I add a 1 second delay it still seems that the view controller is not being rendered until the navigation transition actually starts. I still see a brief flash of white (background color) as the transition happens.

@jbolter it fixed my issue. seems like your JS thread is doing much work, Try to use InteractionManager

I don't think adding a delay before making the controller visible is recommended. On Android we solved this issue easily be display the screen only after the react root view has been populated. Meaning, once the react root view has at least one child - we conclude it's rendered and show it. Perhaps the same logic can be implemented on iOS as well.

@shahen94 I'm doing very little work on the JS thread so I don't think that is the issue. I can reproduce this in a very simple project if that helps debug.

@guyca Your proposed solution that works on Android sounds like a better solution than a delay. Do you have any tips on where I can look to start investigating this solution? I'm familiar with iOS development.

Unfortunately I'm not familiar with the iOS codebase. Based on the solution suggested here, I can only suggest you start looking at RCCViewController and make your way from there.

Guy I can find some time tomorrow to do that on iOS - point me to what you
meant in Android, I don't remember us doing anything like that..

On Apr 25, 2017 11:17 PM, "Guy Carmeli" notifications@github.com wrote:

Unfortunately I'm not familiar with the iOS codebase. Based on the
solution suggested here, I can only suggest you start looking at
RCCViewController and make your way from there.

—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
https://github.com/wix/react-native-navigation/issues/358#issuecomment-297151959,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGWgj1T5oNK3YKO4hb3G71fdv0EyQ1iPks5rzlTAgaJpZM4KPG3x
.

Hey @DanielZlotin,
In ContentView, Once the first child is added to the ReactRootView, we call the onDisplayListener in runOnPreDraw

@DanielZlotin Just a reminder that I would love for you to take a look at this "flashing" issue when yu have time!

@DanielZlotin Would you have time to point me in the right direction? I am familiar with iOS so maybe I could take a crack at this.

Maybe it is possible to postpone route change until Components are rendered. But it sounds more of a 'hack'..

Yeah it's unclear to me why the components aren't being rendered until the view is already appearing. It seems like the view loading is not started soon enough or something along those lines.

Subscribing to this chain as I'm seeing the same issue on iOS (and annoyingly don't have the same background color on the entire screen, so just formatting the background isn't a great fix).

For an internal project we implemented a quickfix by sending a signal in the componentDidMount method of the Screen class and having the navigation controller listen for this before pushing the corresponding view controller. I believe airbnb's native navigation does something comparable. Although it feels hacky, I can share the solution if anybody is interested?

@patrickkempff Sounds good, I am interested. We were forced to downgrade to non-native react-navigation because of this.

@patrickkempff I'd love to see the hacky-way you're doing it! This issue isn't going to stop me from using this navigator, but some of our users have been pointing it out.

Thanks!

@patrickkempff I am also definitely interested in your "hacky" solution to this. I've been meaning to look into this but haven't had a time to create a hack of my own.

Cool! Busy week. I will add the code to github this weekend as soon as possible.

@patrickkempff Any chance you could share that workaround "hack" with us? Thanks!

@patrickkempff It would be a great help if you could share that workaround soon with us. Much appreciated!!

@patrickkempff can u share your hacky solution ?

What is the tricky way? I'm used interaction manager and backgroundColor but nothing change.

I'm confused about something in this thread, so I'd like to confirm..

screenBackgroundColor was added to navigatorStyle and it certainly solves the problem of "white flash on screen push". It gets applied to the UIView on the viewWillAppear event, so this should work as intended.

We're now using 1.1.178, and we don't see this issue. Are you guys talking about v2 ?

@steven-luu I see that as a band-aid. Instead of seeing a white-flash you see a [customized-color]-flash. My app had the top 1/4-1/3 of the page a different color than the rest of the page (the header with a title, image, and search-box with content below) - so then I can set it to either show a white-flash over the header or a colored-flash over the lower body, but not neither (like on Android where there's no flash.

@angusmccloud https://wix.github.io/react-native-navigation/#/styling-the-navigator?id=style-object-format -> screenBackgroundImageName

I'll take a look, thanks @grundmanise

Is there no solution to the actual "flash" other than to hide it with a solid color? With true native iOS development I never see a flash when pushing view controllers. Is this because the react JS engine has to render the screen while the push animation is happening?

Feel like I am seeing similar thing: every pushed screen is white until after the slide in animation, and then it renders? Would it somehow be possible to run the render step ahead of time? Is there some locking / single threaded ness that prevents them / makes it feel too delayed? Thanks for any thoughts. Will be trying the solid color at least for now.

update: the iOS native code hack doesn't work for me oh well.

i also tried screenBackgroundColor, no luck.

i also did NSLog(@"%@", self.navigatorStyle); and it showed only "{ navBarHidden = 1; }" so my javascript attempts to set screenBackgroundColor don't seem to get through? I did it both in the top level navigator options and on each screen to try to make sure it works (since hiding the nav bar doesn't work for me at the top level).

I have tried the componentDidMount hack as well, sadly it doesn't 100% work.
Flicker still happens about 20% of times. I feel like it is not about whether react view loaded or not.

I love this package and still have a faith for the chosen one who will figure this out.

@fokoz @patrickkempff What method were you using with componentDidMount?

My app uses an image of a gradient as its background. I'm currently using a combination of the navigator style props screenBackgroundColor and screenBackgroundImageName, as well as setting the ImageBackground backgroundColor to the dominant gradient colour.

The white flash is gone, but there's a noticeable transition between the single-colour backgroundColor and the gradient image.

@cvarley100
I put some codes into RCCNavigationController by using queue, when I call the push code of RNN(this.props.navigator.push), I hold the transition by queue it in native code and on componentDidmount of the pushed element, I then send some signal to native code to start the normal transition.

Without setTimeout on componentDidmount, the flicker happens about 20-30%, with 100ms setTimeout the percentage has dropped to 15-20%.

One more thing about using screenBackgroundColor, be aware of this issue https://github.com/wix/react-native-navigation/issues/1047

@patrickkempff please share your solution, just copy paste the native code you wrote somewhere, it doesn't matter wether it's nice or anything, but it would be so helpful.

This issue is quite annoying and a hackt fix is certainly much better than the current situations. My users won't care about the code quality, I promise.

Not sure if this will help anyone, but Wix have also made rn-synchronous-render which claims to help with this problem: https://github.com/wix/rn-synchronous-render .

I've been looking for a solution to this too but not yet tried this one.

Yes that was the main idea behind the synchronous render proof of concept. It will probably not solve the problem completely though, as current investigation shows that the render time itself (in js thread) is very slow. Further research is needed.

A workaround for me has been to divide the rendering of my component into different times. First render the easy stuff, like some static cells/headers, then perform the rest of the render after a timeout.

Something like:

constructor(props) {
    super(props);
    var staticItems = [1, 2, 3];
    this.state = {staticItems: staticItems}
}

componentDidMount() {
  setTimeout(function () {
    this.setState(function(prevState){
       return _.merge(prevState, {dynamicItems: Server.getData()})
    })
  }.bind(this), 10);
}

RN will render the static stuff really quickly; quick enough that you won't see the white flash anymore. After that, you can load in the remaining data.

I hope is not too late, but I managed to fix this in iOS by abusing of screenBackgroundImageName and rootBackgroundImageName.

I had the white flash when transitioning to a screen with these styles:

navigatorStyle: {
        // .... other styles
        navBarTranslucent: true,
        navBarTransparent: true,
        drawUnderNavBar: true,
      }

I added the image in Xcode under the Image asset file and then called it by just the name (no require, no "image!name", no "uri: , static".

Here is a temporary fix I have put in place after I copied the repo into our own codebase for the time being. The basic idea is to wait for the React Native view in iOS to finish rendering before pushing it on the navigation stack.

This can however cause a performance issue, a long delay on the push, if you are doing too much work in your components render method. A strategy we implemented on our main views for controllers was to set a timeout of 100ms before doing any heavy lifting. Another strategy was to properly guard from redux updates :)

https://gist.github.com/MPiccinato/7436301a8292af653dfdd628ffaa3139

Line 16: New bool _rendering to signal a controller being pushed is rendering

Line 67: Listens to RCTContentDidAppearNotification and calls contentDidAppear:

Line 72: contentDidAppear dispatches a function on the main queue and sets _rendering to NO

Line 184: Set _rendering to YES prior to pushing the controller

Line 442: Add _rendering to conditional to see if the controller should be queued instead of pushed immediately

Line 451: Set _rendering to NO to allow for the next controller to push

@MPiccinato Thank you for this.

The only issue is that on some screens, there is a flash of the page content in the top left hand corner on navigation push.

Do you know why that might be?

thanks @MPiccinato .. But seem a little heavy.. not sure i will try on my phone.

I have updated the gist to allow for only one VC to be pushed/queued at a time.

@cvarley100 Sorry, I haven't experienced that issue :( The only thing I can think of is some sort of screen sizing, height/width issue in the styles?

@alien3d It is a bit heavy but got us to a dependable working state. Since the library is going through a rewrite we have pulled all of the current branches code into our repo.

@MPiccinato are you mean v2 react native navigation.. I yet not migrate to v2 version.. I have test to my mobile .Pretty slow not usable .. I'm not sure because i redirect to the same screen since upon push to other screen no weird white screen.

@alien3d This is for V1

@MPiccinato I'm so sorry. Your code is good. It's already works but I didn't known. So sorry

Very thanks !

I'm still experiencing the same issue. Will it be fixed in future?

Any movement on this?

Anything?

@nezaidu I don't think there is much dev on v1 happening, I believe v2 is being focused on. You can try my solution here, https://github.com/wix/react-native-navigation/issues/358#issuecomment-337598200

@MPiccinato thanks!

@MPiccinato i try your solution and it did not work. The application seems laggie, i have to press the push screen button two times to change screen, and the white flicker is still there. Anyways thank you

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest version and report back. Thank you for your contributions.

Any fixes here?

For V2 the fix is

  Navigation.setDefaultOptions({
    layout: {
      backgroundColor: 'black'
    }
  })

Just set the background color to the color you are using for background. (In case it’s not an image)

Still having this issue when using an image 😞

@MPiccinato Thanks! There is one problem with your approach and setButtons, because it sets buttons on visible screen, not on the new one. I can add setTimeout for it, but it's not ideal solution. Is there a way to postpone setButtons for corresponding screen until it renders?

Any potential updates/solutions for V2. Setting a backgroundColor doesn't particularly help, because if the backgroundColor is already white, the segue occurs, no content, and then content pops in.

navigatorStyle: { navBarHidden : true, screenBackgroundColor: '#000000' } worked for me. However, navigatorStyle: { navBarHidden : true, screenBackgroundColor: 'black' } did NOT. On v1

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back. Thank you for your contributions.

This is still relevant.

Relevant indeed,

Still a problem.

Guys, any news? Still a problem.

Still a problem with the latest release.
Would be nice to see a fix for this.

Still having this issue on react-native 0.57.3 with react-native-navigation 1.1.493

@guyca unless i'm misunderstanding #3661, this is a totally different issue; #3661 seems to be centered on initial loading of the app, while this issue is centered on the pushing of any new screen.

navigatorStyle: { navBarHidden : true, screenBackgroundColor: '#000000' } worked for me. However, navigatorStyle: { navBarHidden : true, screenBackgroundColor: 'black' } did NOT. On v1

navigatorStyle: {screenBackgroundColor: '[color]' } is definitely your fix on v1. This works great either on push or in screen setup.

@loganthompson what happens when my screen is half one color, half another? what happens when it's a gradient? what happens when it's an image, or buttons, or really anything besides just a solid color? having only a solid color as an option isn't a good solution IMO; screens in native apps are too complex for that.

@guyca I agree with @ryanscottaudio - this is a different issue and still relevant

@ryanscottaudio this can be solved by a secret not documented on push option:

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

It may cause delays in pushing new screen on slower devices though

@jmacioszek how the fuck? Nice find man

@ryanscottaudio this can be solved by a secret not documented on push option:

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

It may cause delays in pushing new screen on slower devices though

This solution does seems to work for showModal animation waitForRender: true property and you can see that is working when setting the boolean from false to true.

What I found is that only partially works on the push animation waitForRender: true at least for v2.13.0. When setting this on push, there is still a white flicker but the flicker seems to happen at a faster speed but is still very noticeable when setting the boolean from false to true.

Any ideas?

I'm seeing this when I switch to another page. I don't know what React native is, but I'm seeing this. Is there a way to fix it?

Any solution to not show the splash in react-navigation version 3

@nihp swith to react-navigation. Far better API and now works smoothly. Switch is not that hard

Any solution for this ?
We are facing same issue & above mention solution does not work for us.
"react-native": "0.61.5",
"react-navigation": "^4.0.10",

We also facing the same issue

@nihp You can use waitForRender option

@guyca I have tried waitForRender in my app. It does not work for me

animations: {
push: {
waitForRender: true
},
showModal: {
waitForRender: true
}
},

@nihp

https://facebook.github.io/react-native/docs/running-on-device#pro-tip

// Place this code after "[self.window makeKeyAndVisible]" and before "return YES;"
UIView* launchScreenView = [[[NSBundle mainBundle] loadNibNamed:@"LaunchScreen" owner:self options:nil] objectAtIndex:0];
launchScreenView.frame = self.window.bounds;
rootView.loadingView = launchScreenView;

@nihp have you tried putting the waitForRender: true whenever you are about to push to a new screen? It worked for me that way, it was the only way i could make it work. Whenever I do a Navigation.push() i set the waitForRender within the options. Works like a charm. Good luck!

@msqar
I have set in my routes as below mentioned code.

Also, I have tried with the each createStackNavigator ==> navigation options too.

{ initialRouteName: 'AuthScreen', headerMode: 'none', waitForRender: true },

I'm using v5.1.1. I've tried the solution

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

But it didn't work. Any idea? :(

Hey guys this issue will be addressed soon, I hope by the end of November. Sorry for the inconvenience.

@savelichalex you're correct, that's exactly what happens. On Android we avoid this issue by showing the pushed screen only after render is done and the native view is populated.

may I know how you did so in Android?

I'm using v5.1.1. I've tried the solution

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

But it didn't work. Any idea? :(

Did you got the solution ??

I've found that using the waitForRender: true can be useful, but also just setting the layout's componentBackgroundColor works for my needs. I'm using RNN 6.12.2 for reference.

options: {
  layout: {
    componentBackgroundColor: Theme.colors.background,
  },
},

In the screenOptions prop passed to Stack.Navigator, setting stackAnimation: "none" fixes the white flashes for me, atleast on Android (physical device). I've yet to examine what effect this tweak might have on other platforms.

Sure, the resulting navigations do become jarringly instant - but I've never heard a user complain that an app is "too snappy" or "responds too quickly" to interactions.

I'm using Expo 39.0.4 with @react-navigation/native version 5.8.10.

Nvm, wrong library (thanks @chrise86). My bad.

In the screenOptions prop passed to Stack.Navigator, setting stackAnimation: "none" fixes the white flashes for me, atleast on Android (physical device). I've yet to examine what effect this tweak might have on other platforms.

Sure, the resulting navigations do become jarringly instant - but I've never heard a user complain that an app is "too snappy" or "responds too quickly" to interactions.

I'm using Expo 39.0.4 with @react-navigation/native version 5.8.10.

@worstpractice that is a different library, not this one.

@ryanscottaudio this can be solved by a secret not documented on push option:

Navigation.push(_, { 
  component: {
     ...,
     options: {
        animations: {
           push: {
              waitForRender: true
           }
        }
     }
  }
})

It may cause delays in pushing new screen on slower devices though

maybe I'm asking a silly question but where should I write this code in the project?

@Abhishek-Sankey I believe you'll need to use that snippet (everything inside the options object) whenever you push a screen that suffers from the white flash and/or you want to wait for the render to complete before the push/navigation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kiroukou picture kiroukou  Â·  3Comments

switchtrue picture switchtrue  Â·  3Comments

zhanguangao picture zhanguangao  Â·  3Comments

swingywc picture swingywc  Â·  3Comments

birkir picture birkir  Â·  3Comments