Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Navigator] Animated replace #1981

Closed
DaleLJefferson opened this issue Jul 13, 2015 · 27 comments · May be fixed by facebookarchive/react-native-custom-components#8
Closed

[Navigator] Animated replace #1981

DaleLJefferson opened this issue Jul 13, 2015 · 27 comments · May be fixed by facebookarchive/react-native-custom-components#8
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@DaleLJefferson
Copy link
Contributor

I often find myself wanting to "replace" a route i.e not adding to the history yet have the route change animate.

In iOS I would do something like this.

[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:someOtherViewController animated:YES];

Maybe this is related to issue #1953 I think I would prefer an api where we could do

replace(route, animated);
push(route, animated);
@jeanregisser
Copy link
Contributor

Was thinking about the same.
If we can agree on an API I would gladly submit a PR implementing it.

@sahrens
Copy link
Contributor

sahrens commented Jul 15, 2015

What do you think, Eric?

@ericvicenti
Copy link
Contributor

I think the API looks reasonable, and we have actually experimented with something similar. We're working on some big changes to Navigator to get the route stack logic out, and to upgrade to the new Animated API. This should be pretty easy to add when we make those changes

@grabbou
Copy link
Contributor

grabbou commented Jul 17, 2015

Animated replace would've been awesome! I often find it useful to replace main view controller when using e.g. side menu. Unfortunately, I am quite limited currently, so waiting for the updates in this matter

@miracle2k
Copy link

I'm doing something like this now to skip a payment screen route that I really don't want in the stack:

      var r = navigator.getCurrentRoutes();
      var backCount = 1;
      if (route.props.fromPayment)
        backCount = 2;
      navigator.popToRoute(r[r.length-1-backCount]);  

It works, but I'd really rather not have this screen have the responsibility to know about the previous routes.

@grabbou
Copy link
Contributor

grabbou commented Sep 9, 2015

@miracle2k for this specific use case, take a look at our implementation https://github.com/este/native/blob/master/src/app/app.react.js#L105-L108. We are decorating Navigator with few handy methods and passing it down, this particular one works quite well.

@ericvicenti
Copy link
Contributor

For now maybe you could do a push and then immediatelyResetRouteStack once the transition completes

@chirag04
Copy link
Contributor

chirag04 commented Oct 6, 2015

@ericvicenti cool. so immediatelyResetRouteStack in didfocus should do the trick?

@miracle2k
Copy link

Inspired by the suggestion from @grabbou, I am now using this workaround:

function betterNavigator(navigator) {
  const navigation = {
    pop: () => {
      debugger;
      const routes = navigator.getCurrentRoutes();

      // Find the next route without replace mode
      var target = null;
      for (let i=routes.length-2; i>=0; i--) {
        if (!routes[i].replace) {
          target = routes[i];
          break;
        }
      }

      if (target)
        navigator.popToRoute(target);
      else
        navigator.pop();
    },
    popToTop: navigator.popToTop,
    popToRoute: navigator.popToRoute,
    popBack: (index) => {
      const routes = navigator.getCurrentRoutes();
      navigator.popToRoute(routes[routes.length - index - 1]);
    },
    replaceAtIndex: navigator.replaceAtIndex,
    push: navigator.push,
    replace: (target) => {
      debugger;
      const routes = navigator.getCurrentRoutes();
      routes[routes.length-1].replace = true;
      navigator.push(target);
    },
    getCurrentRoutes: navigator.getCurrentRoutes
  };
  return navigation;
}

Note the overwrites of replace() and pop(). pop will skip all routes that have been replaced.

@fender
Copy link

fender commented Dec 2, 2015

@miracle2k Although that allows replace to use the animations from push, I don't think it behaves the same. replace() will unmount the current component and then mount the new one immediately.

@DickyT
Copy link

DickyT commented Jan 20, 2016

@miracle2k @fender I found this problem too, the view i want to replace was a view which using camera, and using this way the camera page cannot be unmounted and it is keep using the camera, which makes my App deny by App Store because of something like "using camera background". I am now using setTimeout to push new view back after poping current view.

Hope next official release can fix this.

@ssh-randy
Copy link

I still find this problem as well, when I have sceneconfig set, it does not work for type replace even though it does for pop

@aksonov
Copy link

aksonov commented Feb 4, 2016

Any ETA on this, @ericvicenti ?

@aksonov
Copy link

aksonov commented Feb 4, 2016

Looks like following code works well, could it be used as workaround?

navigator.immediatelyResetRouteStack(navigator.getCurrentRoutes().splice(-1,1));
navigator.push(ROUTE);

@grabbou
Copy link
Contributor

grabbou commented Feb 4, 2016

@chirag04
Copy link
Contributor

chirag04 commented Feb 4, 2016

@grabbou that is going to remove all prev routes i guess.

Few ideas:

  1. One can push first and then popN routes without animating which gives the same effect as animated replace. The back button on pushed however needs to be handled correctly.

  2. One can popN routes without animating and then push and the back button will be automatically adjusted correctly.

Crazy hack:

Navigator.prototype.popNWithoutAnimation = function(n) {
  if (n === 0) {
    return;
  }
  this.state.routeStack.splice(this.state.presentedIndex - n + 1, n);
  this.state.sceneConfigStack.splice(this.state.sceneConfigStack - n + 1, n);
  this.state.presentedIndex = this.state.presentedIndex - n;
  this.state.transitionCb = null;
};

ExNavigator.prototype.popNWithoutAnimation = function(n) {
  return this.__navigator.popNWithoutAnimation(n);
};

@L-Jovi
Copy link

L-Jovi commented Mar 11, 2016

I try another way which execute normally.

this.props.navigator.immediatelyResetRouteStack(this.props.navigator.getCurrentRoutes().slice(0, -1));

hope make sense.

regards.

@vaukalak
Copy link

For now I use this workaround:
https://gist.github.com/vaukalak/40ed12eadf0f605a8ffc4584144f2fdd

then in my code I do:

if(!this.navigatorUtils) {
    this.navigatorUtils = navigatorUtils(this.refs['navigator']);
}
this.navigatorUtils.resetToAnimated(props.screen);

not too elegant, but seems to work :)

@zhaotai
Copy link

zhaotai commented Jun 3, 2016

I have tried all of these solutions above but all didn't work.
So I had to check the Navigator source code while coded on my own. Finally after solving some problems such as flashing and animating, not working at all, replacing without animation though I have written the code, I got the solution.

These are my code below. Most of them are copied from Navigator source code. What I did is just combine them in a right way. Um...of course, they still can be optimized. And they too much hard to understand. Trust me, I won't understand after tonight. But it works. It's enough.
BTW, my RN is 0.27.

Navigator.prototype.replaceWithAnimation = function (route) {
  const activeLength = this.state.presentedIndex + 1;
  const activeStack = this.state.routeStack.slice(0, activeLength);
  const activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
  const nextStack = activeStack.concat([route]);
  const destIndex = nextStack.length - 1;
  const nextSceneConfig = this.props.configureScene(route, nextStack);
  const nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]);

  const replacedStack = activeStack.slice(0, activeLength - 1).concat([route]);
  this._emitWillFocus(nextStack[destIndex]);
  this.setState({
    routeStack: nextStack,
    sceneConfigStack: nextAnimationConfigStack,
  }, () => {
    this._enableScene(destIndex);
    this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity, null, () => {
      this.immediatelyResetRouteStack(replacedStack);
    });
  });
};

After defining that in prototype, you can use it anywhere you want.
Just like
navigator.replaceWithAnimation(route);

@waleedarshad-vf
Copy link

Performace is too slow above provide method, can anyone optimized it

@zhaotai
Copy link

zhaotai commented Jun 28, 2016

@waleedarshad-vf I used it in online production and after compiling them by react native and set to production mode. It's quite fluent. Have you tried the production mode?

@waleedarshad-vf
Copy link

@zhaotai thanks for you reply. I tried it again last day and it is working now. i don't know what happened that time. it might be due to low memory of the device.

@zhaotai
Copy link

zhaotai commented Jun 29, 2016

@waleedarshad-vf Yeah, especially you develop in the simulator. The application memory will increase along with your code complication increasing and as a result the navigating and animation which is implemented by js code instead of native module will become slow and unsmooth.

@waleedarshad-vf
Copy link

waleedarshad-vf commented Jul 1, 2016

@zhaotai one thing to be mention here that it is not as smooth as the push method of Navigator but thanks for Awesome work

@waleedarshad-vf
Copy link

@zhaotai it needs attention to make it as smooth as push method. the tester here points out this jerk animation issue in a single attempt.

@zhaotai
Copy link

zhaotai commented Jul 18, 2016

@waleedarshad-vf I will try, thanks for suggestion. Maybe you can improve it, too.

@mkonicek
Copy link
Contributor

Hi there! This issue is being closed because it has been inactive for a while.

But don't worry, it will live on with ProductPains! Check out its new home: https://productpains.com/post/react-native/navigator-animated-replace

ProductPains helps the community prioritize the most important issues thanks to its voting feature.
It is easy to use - just login with GitHub. GitHub issues have voting too, nevertheless
Product Pains has been very useful in highlighting the top bugs and feature requests:
https://productpains.com/product/react-native?tab=top

Also, if this issue is a bug, please consider sending a pull request with a fix.
We're a small team and rely on the community for bug fixes of issues that don't affect fb apps.

DaniAkash added a commit to DaniAkash/react-native-custom-components that referenced this issue Jun 16, 2017
New feature
This commit will add replaceWithAnimation function to the Navigator component which will allow the user to replace a route while having the default transition animation of the target scene.

fixes facebook/react-native#1981

Implemented based of Stack-Overflow solution - http://stackoverflow.com/questions/40393380/react-native-transition-animation-for-navigator-replace

Test plan
the following repository [ReactNativeNavigationDemo (branch replaceWithAnimation)](https://github.com/DaniAkash/ReactNativeNavigationDemo/tree/replaceWithAnimation) has a page implemented using the replaceWithAnimation function in SplashPage.js which replaces the splash page with the homepage and it is tested with various use cases and found that the component is working as expected.

This Pull request was previously created at facebook/react-native#12881
@facebook facebook locked as resolved and limited conversation to collaborators Jul 22, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 22, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet