React-native-navigation: viewPagerAndroid not working after push and back

11

I use react-native-tab-view , and push to a view ,and back to the last view , the viewPagerAndroid's touch is not working , and the Tab Button clicks without effect
Android:7.0,5.0
react-native : 0.44,
react-native-navigation:1.1.80

hezheop picture hezheop  ·  2 Jun 2017

Most helpful comment

33

Hey guys

I managed to take some time today to look into this issue. I think this is actually a bug in the native ViewPager. It seems to be tracking its layout state when the view is attached. Since the view is already laid out when it's added again to the screen - onLayout isn't called which is where mIsFirstLayout is set to false.
Because the ViewPager can't smooth scroll back to the selected item before onLayout stage is completed (since it doesn't know the item size) - smooth scrolling doesn't work.

We can easily trick the system into measuring and going through the entire layout cycle of the ViewPager (essentially invalidating the view) by changing the dimensions of the ViewPagerAndroid when the visibility of the screen changes.

export default class ViewPagerAndroidScreen extends React.Component {
constructor(props) {
    super(props);
    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
    this.state = {
      visible: true
    }
  }

  onNavigatorEvent(event) {
    if (event.id === 'willAppear') {
      this.setState({
        visible: true
      });
    }
    if (event.id === 'willDisappear') {
      this.setState({
        visible: false
      });
    }
  }

  render() {
    return (
      <ViewPagerAndroid
        style={[styles.viewPager, {flex: this.state.visible ? 1 : 0}]}>
        <View>
          <Text>First page</Text>
        </View>
        <View>
          <Text>Second page</Text>
        </View>
      </ViewPagerAndroid>
    );
  }
}

Perhaps we can introduce a property to navigator.pop which will force the native views the layout

guyca picture guyca  ·  12 Jul 2017

All comments

0

Is the onPress triggering JS side?

Ehesp picture Ehesp  ·  2 Jun 2017
0

I'm having the same issue, whenever I push a view and then come back no onPress work in any component rendered inside the ViewPagerAndroid, and swiping the pager becomes really really slow

enahum picture enahum  ·  2 Jun 2017
0

That is the ViewPagerAndroid 's bug I think , and I solve that by using react-native-tab-view ' s TabViewPagerPan https://github.com/react-native-community/react-native-tab-view

hezheop picture hezheop  ·  3 Jun 2017
0

still happens with the suggested library or any other, including https://github.com/leecade/react-native-swiper and the funny thing is that it only happens on Android, IOS works just fine.

If I instead of pop do a resetTo everything works just fine.

enahum picture enahum  ·  5 Jun 2017
0

I am also experiencing this issue.

kelvinpompey picture kelvinpompey  ·  7 Jun 2017
0

So the way I solved it is that on android instead of doing a push and pop I did a show and dismiss modal

enahum picture enahum  ·  7 Jun 2017
0

Its a bug in ViewPagerAndroid . In ios its works fine as its based on ScrollView .Any library that implements ViewPagerAndroid ,same problem will occur . Using Modal instead of push will work , but not a good solution.

PARAGJYOTI picture PARAGJYOTI  ·  9 Jun 2017
0

this is a critical bug towards android app using viewpager, please help support this ticket guys
Tons of thanks

dzunglht-ibl picture dzunglht-ibl  ·  19 Jun 2017
16

@dzunglht You can try something like this:

import React, { PureComponent } from 'react';
import {
  View,
  Dimensions,
} from 'react-native';

export default class ViewPagerWrapper extends PureComponent {

  constructor(props) {
    super(props);
    this.props.navigator && 
      this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
  }

  onNavigatorEvent (event) {
    switch(event.id) {
      case 'willAppear':
        this._reattach();
        break;
    }
  }

  state = {
    width: Dimensions.get('window').width,
  }

  render() {
    return (
      <View style={[this.props.style, { flex: 1 }]}>
        <View style={{ width: this.state.width, flex: 1 }}>
          {this.props.children}
        </View>
      </View>
    )
  }

  _x = 0.5

  _reattach = () => {
    this.setState({
      width: this.state.width - this._x,
    }, () => {
      this._x *= -1;
    });
  }

and then use:

<ViewPagerWrapper navigator={this.props.navigator}>
  <YourAwesomeComponent>
    //Component that uses ViewPagerAndroid
    ...
  </YourAwesomeComponent>
</ViewPagerWrapper>

Maybe this is not most elegant solution, but it works for me.

t2n3qqq picture t2n3qqq  ·  20 Jun 2017
0

@guyca Please have an official fix asap, this is such a critical bug, above way doesn't work.
Thanks

kelvin-dev picture kelvin-dev  ·  22 Jun 2017
0

look at this https://github.com/jykun/react-native-swiper.git, i change viewPageAndroid to scrollView.

jykun picture jykun  ·  22 Jun 2017
0

@meow0703 @dzunglht Can you guys upload an example where the bug is reproducible? I doubt this is a bug in RNN, it's probably an issue in the actual component which will require a PR to RN.

guyca picture guyca  ·  22 Jun 2017
0

I just came here to confirm that this happens to me too.

mannol picture mannol  ·  22 Jun 2017
0

@guyca Steps to reproduce:

  • create root with startTabBasedApp() with x tabs
  • in one tab, render ViewPagerAndroid
  • from that tab push some screen with this.props.navigator.push()
  • press back on the newly pushed screen
  • the ViewPagerAndroid no longer works
mannol picture mannol  ·  22 Jun 2017
0

Dear folks,
8 days passed, we're looking for your official fix asap
Many thanks

dzunglht-ibl picture dzunglht-ibl  ·  3 Jul 2017
0

Dear wix team , can you make a alternative viewPager component to
ViewPagerAndroid . ViewpagerAndroid has only few features that doesnot
include pagePadding or snapped carousel like features . I know
react-native-navigation has a built in Viewpager class used for TopTabs
scrolling . So please create a top lavel Viewpager Component .

@guyca please , think about it.

On 03-Jul-2017 3:09 PM, "dzunglht" notifications@github.com wrote:

Dear folks,
8 days passed, we're looking for your official fix asap
Many thanks


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/wix/react-native-navigation/issues/1317#issuecomment-312598982,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AKYl_wb7kdKCC-wsDdOATDhSUm8NBNiSks5sKLbqgaJpZM4Nt0y9
.

PARAGJYOTI picture PARAGJYOTI  ·  3 Jul 2017
0

@all @guyca Please have a look on below elegant library (built for react-native in mind) and refer how efficiently they use their viewPager, we should extend and/or make use of their effort. Hope that help.
Ref: https://github.com/appintheair/react-native-looped-carousel

kelvin-dev picture kelvin-dev  ·  3 Jul 2017
0

@guyca where are you now, when we have official fix for the ticket ?

dzunglht-ibl picture dzunglht-ibl  ·  6 Jul 2017
1

If you guys are waiting on this internally or something then I'd try and look at alternative options for now, there is no timeline on when this will be fixed.

Ehesp picture Ehesp  ·  6 Jul 2017
33

Hey guys

I managed to take some time today to look into this issue. I think this is actually a bug in the native ViewPager. It seems to be tracking its layout state when the view is attached. Since the view is already laid out when it's added again to the screen - onLayout isn't called which is where mIsFirstLayout is set to false.
Because the ViewPager can't smooth scroll back to the selected item before onLayout stage is completed (since it doesn't know the item size) - smooth scrolling doesn't work.

We can easily trick the system into measuring and going through the entire layout cycle of the ViewPager (essentially invalidating the view) by changing the dimensions of the ViewPagerAndroid when the visibility of the screen changes.

export default class ViewPagerAndroidScreen extends React.Component {
constructor(props) {
    super(props);
    this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
    this.state = {
      visible: true
    }
  }

  onNavigatorEvent(event) {
    if (event.id === 'willAppear') {
      this.setState({
        visible: true
      });
    }
    if (event.id === 'willDisappear') {
      this.setState({
        visible: false
      });
    }
  }

  render() {
    return (
      <ViewPagerAndroid
        style={[styles.viewPager, {flex: this.state.visible ? 1 : 0}]}>
        <View>
          <Text>First page</Text>
        </View>
        <View>
          <Text>Second page</Text>
        </View>
      </ViewPagerAndroid>
    );
  }
}

Perhaps we can introduce a property to navigator.pop which will force the native views the layout

guyca picture guyca  ·  12 Jul 2017
0

Thanx @guyca . Finally . What a relief . Can you please please build a library or include in react-native-navigation for Snapped RecyclerView . I haven't found any library for Recyclerview in react-native especially for a horizontal swiper that play-store uses .

PARAGJYOTI picture PARAGJYOTI  ·  13 Jul 2017
0

The above solution didn't work for me. I went ahead and used a Modal and it worked flawlessly.

andrerfneves picture andrerfneves  ·  17 Jul 2017
1

@andrerfneves Modal is displayed over current screen - so the ViewPager isn't detached from window (which is the root cause of this bug)

guyca picture guyca  ·  18 Jul 2017
0

@guyca I'm aware of the reason why Modals work. I'm stating that the solution you provided above didn't work for me, aka re-render/calculating height/width again didn't solve my use cause. Just in case other people go through the trouble of implementing your solution.

andrerfneves picture andrerfneves  ·  20 Jul 2017
2

I'm here to confirm that @guyca's solution works. Thanks!

mannol picture mannol  ·  24 Jul 2017
0

I am using https://github.com/archriss/react-native-snap-carousel as an alternative and it is working pretty well.

kelvinpompey picture kelvinpompey  ·  24 Jul 2017
0

@kelvinpompey the reason for using viewPagerAndroid (or any control based on it) is that RefreshControl does not work with paged ScrollView

mannol picture mannol  ·  24 Jul 2017
0

@guyca when we have the official fix in next release ? Newbiews don't have time to search out this ticket
Thanks

kelvin-dev picture kelvin-dev  ·  25 Jul 2017
0

...annnnnnnd it is still unpatched. I have same issue.
Thanks to @t2n3qqq for the workaround, it works great for me.

drpiou picture drpiou  ·  16 Aug 2017
0

I have same issue. Is there an official fix? Temporally I am using the forks mentioned above.

rodribech20 picture rodribech20  ·  28 Sep 2017
0

follow

rodolfobarretoweb picture rodolfobarretoweb  ·  30 Sep 2017
-6

@here @all @guyca Dear the responsible author, please resolve the ticket issue and put it into the release roadmap guys, please resolve the ticket, NOT try finding a workaround !
Tons of thanks

dzunglht-ibl picture dzunglht-ibl  ·  8 Oct 2017
13

@dzunglht this is an open source initiative. Software development is what it is today because people like @guyca (and companies like Wix) and others, spend countless hours devoting their energy and knowledge to create these complex solutions so we can build better applications.

Don't demand that things get fixed.

Fix it yourself, or use the workaround pointed above.

andrerfneves picture andrerfneves  ·  9 Oct 2017
1

Hi @kelvinpompey I am using react-native-snap-carousel but whenever I import Carousel I got this error:

screen shot 2017-10-21 at 18 40 32

Have you had this issue by any chance or anyone?
Is there any workaround it?

Thanks

heron2014 picture heron2014  ·  21 Oct 2017
0

@t2n3qqq Thank you , it also works great for me

mushishixian picture mushishixian  ·  25 Oct 2017
0

guyca commented on 12 Jul -That Solution works for me for "react-native-tab-view".
I use {flex: this.state.visible ? 1 : 0} for container of TabViewAnimated.

So i have:

<View style={{flex: this.state.visible ? 1 : 0}}> <TabViewAnimated ... /> </View>

iksent picture iksent  ·  7 Nov 2017
0

@heron2014 did you get is to work

smooJitter picture smooJitter  ·  14 Nov 2017
0

@smooJitter yes, I have upgraded RN to 0.48.0 which I was due anyway and it started to work without any workaround :)

heron2014 picture heron2014  ·  14 Nov 2017
0

I confirm @guyca solution fixes the issue with React-Native-ViewPager on Android.
Although, it makes the animation hangs, on both platforms :/

I'm using this to only use the fix on Android

const flex = Platform.select({ios: {flex: 1}, android: {flex: this.state.visible ? 1 : 0}});
<ViewPager style={{...flex}}/>
Annihil picture Annihil  ·  28 Feb 2018
0

image
image

In the push
RelativeLayout.removeView( previous view )
In the pop
RelativeLayout.addview(previous view)

RelativeLayout extends ViewGroup

In the native
ViewPager is removed by ViewGroup and added to

Will there be this bug

janiokq picture janiokq  ·  1 Mar 2018
0

You can

image
Function is the
private void pushScreenToVisibleStack(LayoutParams layoutParams,
final Screen nextScreen,
final Screen previousScreen,
@Nullable final Promise onPushComplete,
@Nullable final Screen.OnDisplayListener onDisplay, final Boolean runanime)

image
Function is the
public void pop(final boolean animated, final double jsPopTimestamp, @Nullable final OnScreenPop onScreenPop)

image
Function is the
private void swapScreens(boolean animated, final Screen toRemove, Screen previous, OnScreenPop onScreenPop)

All changes in
com.reactnativenavigation.screens ->ScreenStack.java

The problem will be repaired

But when multiple pages exist, the graphics memory will be high

Because all pages are displayed on the page

Do anyone have a better solution? I'm a JavaScript code farm native code that isn't very well understood.

janiokq picture janiokq  ·  1 Mar 2018
3

I found the real reason

In the native

react-native-navigation
The implementation is to add and remove views in a ViewGroup

I saw the implementation of ViewPager
image

mFirstLayout It's this guy
This state is used to record whether the viewpager is the first initialization of the layout

He has such a piece of code
image

When navigation is switched on the page
Viewpager is removed from the view and added
It triggered this event
mFirstLayout = true;

Viewpager can't roll.
You can do this

image
Using this class to use viewpager

There's no problem.

janiokq picture janiokq  ·  2 Mar 2018
0

@janiokq How did you use a custom class for your ViewPager, please explain how to do

Annihil picture Annihil  ·  3 Mar 2018
0

@janiokq Good work finding the problem - is this a solution that works for you? And if so, please could you explain how we can do the same?

gusgoose picture gusgoose  ·  23 Apr 2018
0

@t2n3qqq this solution works for me. But if you have multiple carousel/ViewpagerAndroid in a screen then instead of setOnNavigatorEvent you have to use addOnNavigatorEvent

this.props.navigator.addOnNavigatorEvent(this.onNavigatorEvent.bind(this));

ashokkumar88 picture ashokkumar88  ·  1 May 2018
0

In case anyone is still encountering this issue while using react-native-tab-view, I would suggest using PagerScroll instead of PagerAndroid, unless you really need PagerAndroid for some reason. I am developing an app for both iOS and Android and initially chose PagerScroll for iOS and PagerAndroid for Android and encountered this unexpected bug only on Android. After some research I thought I'd just try changing the pager and thus far it seems to work flawlessly. Hope this might help someone!

paulbargaoanu picture paulbargaoanu  ·  13 Jun 2018
0

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.

stale[bot] picture stale[bot]  ·  28 Jul 2018
0

The issue has been closed for inactivity.

stale[bot] picture stale[bot]  ·  4 Aug 2018
0

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.

stale[bot] picture stale[bot]  ·  14 Oct 2018
0

The issue has been closed for inactivity.

stale[bot] picture stale[bot]  ·  21 Oct 2018
2

Hi everyone,

The library 'react-native-tab-view' uses React Native's ViewPagerAndroid as default pager for Android and this component is not working well.

I've found an easy SOLUTION that made my app run without any problem. I just had to tell the 'react-native-tab-view' to use the PagerScroll (which is the default for iOS and its based on RN's ScrollView) and that's it. To do that, you just import {PagerScroll} from 'react-native-tab-view' pass the following prop to the main TabView component:

<TabView
   ...
   renderPager={(props) => <PagerScroll {...props}/>}
   ...
/>

Also you may want to pass this prop too:

swipeEnabled={false}

this will evade some conflicts for example when you are using side drawers on 'react-native-navigation' because they appear by swiping too.

edgarbonillag picture edgarbonillag  ·  12 Jan 2019
0

Hi! Can someone guide me how to pass react-native-navigation screens (Navigation.registerComponent(Screens.ScreenA, () => require('./ScreenA'));) to use with react native tab SceneMap? I'm having having error 'invariant violation: native module can not be null' using in RNNv3. Thanks!

const renderScene = SceneMap({
    first: Screens.ScreenA,
    second: Screens.ScreenB
  });

....

render() {
    return (
      <View>
        <TabView
            navigationState={{ index, routes }}
            renderScene={renderScene}
            onIndexChange={setIndex}
            initialLayout={initialLayout}
          />
     </View>
    );
  }
rat-moonshine picture rat-moonshine  ·  29 Apr 2020